Completed
Push — master ( 528424...ed1647 )
by
unknown
22:54
created
apps/files_sharing/tests/Controller/ShareAPIControllerTest.php 1 patch
Indentation   +5568 added lines, -5568 removed lines patch added patch discarded remove patch
@@ -66,517 +66,517 @@  discard block
 block discarded – undo
66 66
  * @group DB
67 67
  */
68 68
 class ShareAPIControllerTest extends TestCase {
69
-	use EmailValidatorTrait;
70
-
71
-	private string $appName = 'files_sharing';
72
-	private string $currentUser;
73
-
74
-	private ShareAPIController $ocs;
75
-
76
-	private IManager&MockObject $shareManager;
77
-	private IGroupManager&MockObject $groupManager;
78
-	private IUserManager&MockObject $userManager;
79
-	private IRequest&MockObject $request;
80
-	private IRootFolder&MockObject $rootFolder;
81
-	private IURLGenerator&MockObject $urlGenerator;
82
-	private IL10N&MockObject $l;
83
-	private IConfig&MockObject $config;
84
-	private IAppConfig&MockObject $appConfig;
85
-	private IAppManager&MockObject $appManager;
86
-	private ContainerInterface&MockObject $serverContainer;
87
-	private IUserStatusManager&MockObject $userStatusManager;
88
-	private IPreview&MockObject $previewManager;
89
-	private IDateTimeZone&MockObject $dateTimeZone;
90
-	private LoggerInterface&MockObject $logger;
91
-	private IProviderFactory&MockObject $factory;
92
-	private IMailer&MockObject $mailer;
93
-	private ITagManager&MockObject $tagManager;
94
-	private TrustedServers&MockObject $trustedServers;
95
-
96
-	protected function setUp(): void {
97
-		$this->shareManager = $this->createMock(IManager::class);
98
-		$this->shareManager
99
-			->expects($this->any())
100
-			->method('shareApiEnabled')
101
-			->willReturn(true);
102
-		$this->shareManager
103
-			->expects($this->any())
104
-			->method('shareProviderExists')->willReturn(true);
105
-		$this->groupManager = $this->createMock(IGroupManager::class);
106
-		$this->userManager = $this->createMock(IUserManager::class);
107
-		$this->request = $this->createMock(IRequest::class);
108
-		$this->rootFolder = $this->createMock(IRootFolder::class);
109
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
110
-		$this->currentUser = 'currentUser';
111
-
112
-		$this->l = $this->createMock(IL10N::class);
113
-		$this->l->method('t')
114
-			->willReturnCallback(function ($text, $parameters = []) {
115
-				return vsprintf($text, $parameters);
116
-			});
117
-		$this->config = $this->createMock(IConfig::class);
118
-		$this->appConfig = $this->createMock(IAppConfig::class);
119
-		$this->appManager = $this->createMock(IAppManager::class);
120
-		$this->serverContainer = $this->createMock(ContainerInterface::class);
121
-		$this->userStatusManager = $this->createMock(IUserStatusManager::class);
122
-		$this->previewManager = $this->createMock(IPreview::class);
123
-		$this->previewManager->method('isAvailable')
124
-			->willReturnCallback(function ($fileInfo) {
125
-				return $fileInfo->getMimeType() === 'mimeWithPreview';
126
-			});
127
-		$this->dateTimeZone = $this->createMock(IDateTimeZone::class);
128
-		$this->logger = $this->createMock(LoggerInterface::class);
129
-		$this->factory = $this->createMock(IProviderFactory::class);
130
-		$this->mailer = $this->createMock(IMailer::class);
131
-		$this->tagManager = $this->createMock(ITagManager::class);
132
-		$this->trustedServers = $this->createMock(TrustedServers::class);
133
-
134
-		$this->ocs = new ShareAPIController(
135
-			$this->appName,
136
-			$this->request,
137
-			$this->shareManager,
138
-			$this->groupManager,
139
-			$this->userManager,
140
-			$this->rootFolder,
141
-			$this->urlGenerator,
142
-			$this->l,
143
-			$this->config,
144
-			$this->appConfig,
145
-			$this->appManager,
146
-			$this->serverContainer,
147
-			$this->userStatusManager,
148
-			$this->previewManager,
149
-			$this->dateTimeZone,
150
-			$this->logger,
151
-			$this->factory,
152
-			$this->mailer,
153
-			$this->tagManager,
154
-			$this->getEmailValidatorWithStrictEmailCheck(),
155
-			$this->trustedServers,
156
-			$this->currentUser,
157
-		);
158
-
159
-	}
160
-
161
-	/**
162
-	 * @return ShareAPIController&MockObject
163
-	 */
164
-	private function mockFormatShare() {
165
-		return $this->getMockBuilder(ShareAPIController::class)
166
-			->setConstructorArgs([
167
-				$this->appName,
168
-				$this->request,
169
-				$this->shareManager,
170
-				$this->groupManager,
171
-				$this->userManager,
172
-				$this->rootFolder,
173
-				$this->urlGenerator,
174
-				$this->l,
175
-				$this->config,
176
-				$this->appConfig,
177
-				$this->appManager,
178
-				$this->serverContainer,
179
-				$this->userStatusManager,
180
-				$this->previewManager,
181
-				$this->dateTimeZone,
182
-				$this->logger,
183
-				$this->factory,
184
-				$this->mailer,
185
-				$this->tagManager,
186
-				$this->getEmailValidatorWithStrictEmailCheck(),
187
-				$this->trustedServers,
188
-				$this->currentUser,
189
-			])->onlyMethods(['formatShare'])
190
-			->getMock();
191
-	}
192
-
193
-	private function newShare() {
194
-		return Server::get(IManager::class)->newShare();
195
-	}
196
-
197
-
198
-	private function mockShareAttributes() {
199
-		$formattedShareAttributes = [
200
-			[
201
-				'scope' => 'permissions',
202
-				'key' => 'download',
203
-				'value' => true
204
-			]
205
-		];
206
-
207
-		$shareAttributes = $this->createMock(IShareAttributes::class);
208
-		$shareAttributes->method('toArray')->willReturn($formattedShareAttributes);
209
-		$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
210
-
211
-		// send both IShare attributes class and expected json string
212
-		return [$shareAttributes, \json_encode($formattedShareAttributes)];
213
-	}
214
-
215
-	public function testDeleteShareShareNotFound(): void {
216
-		$this->expectException(OCSNotFoundException::class);
217
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
218
-
219
-		$this->shareManager
220
-			->expects($this->exactly(7))
221
-			->method('getShareById')
222
-			->willReturnCallback(function ($id): void {
223
-				if ($id === 'ocinternal:42' || $id === 'ocRoomShare:42' || $id === 'ocFederatedSharing:42' || $id === 'ocCircleShare:42' || $id === 'ocMailShare:42' || $id === 'deck:42' || $id === 'sciencemesh:42') {
224
-					throw new ShareNotFound();
225
-				} else {
226
-					throw new \Exception();
227
-				}
228
-			});
229
-
230
-		$this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
231
-
232
-		$this->ocs->deleteShare(42);
233
-	}
234
-
235
-	public function testDeleteShare(): void {
236
-		$node = $this->getMockBuilder(File::class)->getMock();
237
-
238
-		$share = $this->newShare();
239
-		$share->setSharedBy($this->currentUser)
240
-			->setNode($node);
241
-		$this->shareManager
242
-			->expects($this->once())
243
-			->method('getShareById')
244
-			->with('ocinternal:42')
245
-			->willReturn($share);
246
-		$this->shareManager
247
-			->expects($this->once())
248
-			->method('deleteShare')
249
-			->with($share);
250
-
251
-		$node->expects($this->once())
252
-			->method('lock')
253
-			->with(ILockingProvider::LOCK_SHARED);
254
-
255
-		$expected = new DataResponse();
256
-		$result = $this->ocs->deleteShare(42);
257
-
258
-		$this->assertInstanceOf(get_class($expected), $result);
259
-		$this->assertEquals($expected->getData(), $result->getData());
260
-	}
261
-
262
-
263
-	public function testDeleteShareLocked(): void {
264
-		$this->expectException(OCSNotFoundException::class);
265
-		$this->expectExceptionMessage('Could not delete share');
266
-
267
-		$node = $this->getMockBuilder(File::class)->getMock();
268
-		$node->method('getId')->willReturn(1);
269
-
270
-		$share = $this->newShare();
271
-		$share->setNode($node);
272
-
273
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
274
-		$this->rootFolder->method('getUserFolder')
275
-			->with($this->currentUser)
276
-			->willReturn($userFolder);
277
-
278
-		$userFolder->method('getById')
279
-			->with($share->getNodeId())
280
-			->willReturn([$node]);
281
-
282
-		$this->shareManager
283
-			->expects($this->once())
284
-			->method('getShareById')
285
-			->with('ocinternal:42')
286
-			->willReturn($share);
287
-
288
-		$this->shareManager
289
-			->expects($this->never())
290
-			->method('deleteShare')
291
-			->with($share);
292
-
293
-		$node->expects($this->once())
294
-			->method('lock')
295
-			->with(ILockingProvider::LOCK_SHARED)
296
-			->willThrowException(new LockedException('mypath'));
297
-
298
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
299
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
300
-
301
-		$this->ocs->deleteShare(42);
302
-	}
303
-
304
-	/**
305
-	 * You can always remove a share that was shared with you
306
-	 */
307
-	public function testDeleteShareWithMe(): void {
308
-		$node = $this->getMockBuilder(File::class)->getMock();
309
-
310
-		$share = $this->newShare();
311
-		$share->setSharedWith($this->currentUser)
312
-			->setShareType(IShare::TYPE_USER)
313
-			->setNode($node);
314
-
315
-		$this->shareManager
316
-			->expects($this->once())
317
-			->method('getShareById')
318
-			->with('ocinternal:42')
319
-			->willReturn($share);
320
-
321
-		$this->shareManager
322
-			->expects($this->once())
323
-			->method('deleteShare')
324
-			->with($share);
325
-
326
-		$node->expects($this->once())
327
-			->method('lock')
328
-			->with(ILockingProvider::LOCK_SHARED);
329
-
330
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
331
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
332
-
333
-		$this->ocs->deleteShare(42);
334
-	}
335
-
336
-	/**
337
-	 * You can always delete a share you own
338
-	 */
339
-	public function testDeleteShareOwner(): void {
340
-		$node = $this->getMockBuilder(File::class)->getMock();
341
-
342
-		$share = $this->newShare();
343
-		$share->setSharedBy($this->currentUser)
344
-			->setNode($node);
345
-
346
-		$this->shareManager
347
-			->expects($this->once())
348
-			->method('getShareById')
349
-			->with('ocinternal:42')
350
-			->willReturn($share);
351
-
352
-		$this->shareManager
353
-			->expects($this->once())
354
-			->method('deleteShare')
355
-			->with($share);
356
-
357
-		$node->expects($this->once())
358
-			->method('lock')
359
-			->with(ILockingProvider::LOCK_SHARED);
360
-
361
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
362
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
363
-
364
-		$this->ocs->deleteShare(42);
365
-	}
366
-
367
-	/**
368
-	 * You can always delete a share when you own
369
-	 * the file path it belong to
370
-	 */
371
-	public function testDeleteShareFileOwner(): void {
372
-		$node = $this->getMockBuilder(File::class)->getMock();
373
-		$node->method('getId')->willReturn(1);
374
-
375
-		$share = $this->newShare();
376
-		$share->setShareOwner($this->currentUser)
377
-			->setNode($node);
378
-
379
-		$this->shareManager
380
-			->expects($this->once())
381
-			->method('getShareById')
382
-			->with('ocinternal:42')
383
-			->willReturn($share);
384
-
385
-		$this->shareManager
386
-			->expects($this->once())
387
-			->method('deleteShare')
388
-			->with($share);
389
-
390
-		$node->expects($this->once())
391
-			->method('lock')
392
-			->with(ILockingProvider::LOCK_SHARED);
393
-
394
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
395
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
396
-
397
-		$this->ocs->deleteShare(42);
398
-	}
399
-
400
-	/**
401
-	 * You can remove (the mountpoint, not the share)
402
-	 * a share if you're in the group the share is shared with
403
-	 */
404
-	public function testDeleteSharedWithMyGroup(): void {
405
-		$node = $this->getMockBuilder(File::class)->getMock();
406
-		$node->method('getId')->willReturn(1);
407
-
408
-		$share = $this->newShare();
409
-		$share->setShareType(IShare::TYPE_GROUP)
410
-			->setSharedWith('group')
411
-			->setNode($node);
412
-
413
-		$this->shareManager
414
-			->expects($this->once())
415
-			->method('getShareById')
416
-			->with('ocinternal:42')
417
-			->willReturn($share);
418
-
419
-		// canDeleteShareFromSelf
420
-		$user = $this->createMock(IUser::class);
421
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
422
-		$this->groupManager
423
-			->method('get')
424
-			->with('group')
425
-			->willReturn($group);
426
-		$this->userManager
427
-			->method('get')
428
-			->with($this->currentUser)
429
-			->willReturn($user);
430
-		$group->method('inGroup')
431
-			->with($user)
432
-			->willReturn(true);
433
-
434
-		$node->expects($this->once())
435
-			->method('lock')
436
-			->with(ILockingProvider::LOCK_SHARED);
437
-
438
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
439
-		$this->rootFolder->method('getUserFolder')
440
-			->with($this->currentUser)
441
-			->willReturn($userFolder);
442
-
443
-		$userFolder->method('getById')
444
-			->with($share->getNodeId())
445
-			->willReturn([$share->getNode()]);
446
-
447
-		$this->shareManager->expects($this->once())
448
-			->method('deleteFromSelf')
449
-			->with($share, $this->currentUser);
450
-
451
-		$this->shareManager->expects($this->never())
452
-			->method('deleteShare');
453
-
454
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
455
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
456
-
457
-		$this->ocs->deleteShare(42);
458
-	}
459
-
460
-	/**
461
-	 * You cannot remove a share if you're not
462
-	 * in the group the share is shared with
463
-	 */
464
-	public function testDeleteSharedWithGroupIDontBelongTo(): void {
465
-		$this->expectException(OCSNotFoundException::class);
466
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
467
-
468
-		$node = $this->getMockBuilder(File::class)->getMock();
469
-		$node->method('getId')->willReturn(42);
470
-
471
-		$share = $this->newShare();
472
-		$share->setShareType(IShare::TYPE_GROUP)
473
-			->setSharedWith('group')
474
-			->setNode($node);
475
-
476
-		$this->shareManager
477
-			->expects($this->once())
478
-			->method('getShareById')
479
-			->with('ocinternal:42')
480
-			->willReturn($share);
481
-
482
-		// canDeleteShareFromSelf
483
-		$user = $this->createMock(IUser::class);
484
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
485
-		$this->groupManager
486
-			->method('get')
487
-			->with('group')
488
-			->willReturn($group);
489
-		$this->userManager
490
-			->method('get')
491
-			->with($this->currentUser)
492
-			->willReturn($user);
493
-		$group->method('inGroup')
494
-			->with($user)
495
-			->willReturn(false);
496
-
497
-		$node->expects($this->once())
498
-			->method('lock')
499
-			->with(ILockingProvider::LOCK_SHARED);
500
-
501
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
502
-		$this->rootFolder->method('getUserFolder')
503
-			->with($this->currentUser)
504
-			->willReturn($userFolder);
505
-
506
-		$userFolder->method('getById')
507
-			->with($share->getNodeId())
508
-			->willReturn([$share->getNode()]);
509
-
510
-		$this->shareManager->expects($this->never())
511
-			->method('deleteFromSelf');
512
-
513
-		$this->shareManager->expects($this->never())
514
-			->method('deleteShare');
515
-
516
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
517
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
518
-
519
-		$this->ocs->deleteShare(42);
520
-	}
521
-
522
-	public function testDeleteShareOwnerless(): void {
523
-		$ocs = $this->mockFormatShare();
524
-
525
-		$mount = $this->createMock(IShareOwnerlessMount::class);
526
-
527
-		$file = $this->createMock(File::class);
528
-		$file
529
-			->expects($this->exactly(2))
530
-			->method('getPermissions')
531
-			->willReturn(Constants::PERMISSION_SHARE);
532
-		$file
533
-			->expects($this->once())
534
-			->method('getMountPoint')
535
-			->willReturn($mount);
536
-
537
-		$userFolder = $this->createMock(Folder::class);
538
-		$userFolder->method('getById')
539
-			->with(2)
540
-			->willReturn([$file]);
541
-		$userFolder->method('getFirstNodeById')
542
-			->with(2)
543
-			->willReturn($file);
544
-
545
-		$this->rootFolder
546
-			->method('getUserFolder')
547
-			->with($this->currentUser)
548
-			->willReturn($userFolder);
549
-
550
-		$share = $this->createMock(IShare::class);
551
-		$share
552
-			->expects($this->once())
553
-			->method('getNode')
554
-			->willReturn($file);
555
-		$share
556
-			->expects($this->exactly(2))
557
-			->method('getNodeId')
558
-			->willReturn(2);
559
-		$share
560
-			->expects($this->exactly(2))
561
-			->method('getPermissions')
562
-			->willReturn(Constants::PERMISSION_SHARE);
563
-
564
-		$this->shareManager
565
-			->expects($this->once())
566
-			->method('getShareById')
567
-			->with('ocinternal:1', $this->currentUser)
568
-			->willReturn($share);
569
-
570
-		$this->shareManager
571
-			->expects($this->once())
572
-			->method('deleteShare')
573
-			->with($share);
574
-
575
-		$result = $ocs->deleteShare(1);
576
-		$this->assertInstanceOf(DataResponse::class, $result);
577
-	}
578
-
579
-	/*
69
+    use EmailValidatorTrait;
70
+
71
+    private string $appName = 'files_sharing';
72
+    private string $currentUser;
73
+
74
+    private ShareAPIController $ocs;
75
+
76
+    private IManager&MockObject $shareManager;
77
+    private IGroupManager&MockObject $groupManager;
78
+    private IUserManager&MockObject $userManager;
79
+    private IRequest&MockObject $request;
80
+    private IRootFolder&MockObject $rootFolder;
81
+    private IURLGenerator&MockObject $urlGenerator;
82
+    private IL10N&MockObject $l;
83
+    private IConfig&MockObject $config;
84
+    private IAppConfig&MockObject $appConfig;
85
+    private IAppManager&MockObject $appManager;
86
+    private ContainerInterface&MockObject $serverContainer;
87
+    private IUserStatusManager&MockObject $userStatusManager;
88
+    private IPreview&MockObject $previewManager;
89
+    private IDateTimeZone&MockObject $dateTimeZone;
90
+    private LoggerInterface&MockObject $logger;
91
+    private IProviderFactory&MockObject $factory;
92
+    private IMailer&MockObject $mailer;
93
+    private ITagManager&MockObject $tagManager;
94
+    private TrustedServers&MockObject $trustedServers;
95
+
96
+    protected function setUp(): void {
97
+        $this->shareManager = $this->createMock(IManager::class);
98
+        $this->shareManager
99
+            ->expects($this->any())
100
+            ->method('shareApiEnabled')
101
+            ->willReturn(true);
102
+        $this->shareManager
103
+            ->expects($this->any())
104
+            ->method('shareProviderExists')->willReturn(true);
105
+        $this->groupManager = $this->createMock(IGroupManager::class);
106
+        $this->userManager = $this->createMock(IUserManager::class);
107
+        $this->request = $this->createMock(IRequest::class);
108
+        $this->rootFolder = $this->createMock(IRootFolder::class);
109
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
110
+        $this->currentUser = 'currentUser';
111
+
112
+        $this->l = $this->createMock(IL10N::class);
113
+        $this->l->method('t')
114
+            ->willReturnCallback(function ($text, $parameters = []) {
115
+                return vsprintf($text, $parameters);
116
+            });
117
+        $this->config = $this->createMock(IConfig::class);
118
+        $this->appConfig = $this->createMock(IAppConfig::class);
119
+        $this->appManager = $this->createMock(IAppManager::class);
120
+        $this->serverContainer = $this->createMock(ContainerInterface::class);
121
+        $this->userStatusManager = $this->createMock(IUserStatusManager::class);
122
+        $this->previewManager = $this->createMock(IPreview::class);
123
+        $this->previewManager->method('isAvailable')
124
+            ->willReturnCallback(function ($fileInfo) {
125
+                return $fileInfo->getMimeType() === 'mimeWithPreview';
126
+            });
127
+        $this->dateTimeZone = $this->createMock(IDateTimeZone::class);
128
+        $this->logger = $this->createMock(LoggerInterface::class);
129
+        $this->factory = $this->createMock(IProviderFactory::class);
130
+        $this->mailer = $this->createMock(IMailer::class);
131
+        $this->tagManager = $this->createMock(ITagManager::class);
132
+        $this->trustedServers = $this->createMock(TrustedServers::class);
133
+
134
+        $this->ocs = new ShareAPIController(
135
+            $this->appName,
136
+            $this->request,
137
+            $this->shareManager,
138
+            $this->groupManager,
139
+            $this->userManager,
140
+            $this->rootFolder,
141
+            $this->urlGenerator,
142
+            $this->l,
143
+            $this->config,
144
+            $this->appConfig,
145
+            $this->appManager,
146
+            $this->serverContainer,
147
+            $this->userStatusManager,
148
+            $this->previewManager,
149
+            $this->dateTimeZone,
150
+            $this->logger,
151
+            $this->factory,
152
+            $this->mailer,
153
+            $this->tagManager,
154
+            $this->getEmailValidatorWithStrictEmailCheck(),
155
+            $this->trustedServers,
156
+            $this->currentUser,
157
+        );
158
+
159
+    }
160
+
161
+    /**
162
+     * @return ShareAPIController&MockObject
163
+     */
164
+    private function mockFormatShare() {
165
+        return $this->getMockBuilder(ShareAPIController::class)
166
+            ->setConstructorArgs([
167
+                $this->appName,
168
+                $this->request,
169
+                $this->shareManager,
170
+                $this->groupManager,
171
+                $this->userManager,
172
+                $this->rootFolder,
173
+                $this->urlGenerator,
174
+                $this->l,
175
+                $this->config,
176
+                $this->appConfig,
177
+                $this->appManager,
178
+                $this->serverContainer,
179
+                $this->userStatusManager,
180
+                $this->previewManager,
181
+                $this->dateTimeZone,
182
+                $this->logger,
183
+                $this->factory,
184
+                $this->mailer,
185
+                $this->tagManager,
186
+                $this->getEmailValidatorWithStrictEmailCheck(),
187
+                $this->trustedServers,
188
+                $this->currentUser,
189
+            ])->onlyMethods(['formatShare'])
190
+            ->getMock();
191
+    }
192
+
193
+    private function newShare() {
194
+        return Server::get(IManager::class)->newShare();
195
+    }
196
+
197
+
198
+    private function mockShareAttributes() {
199
+        $formattedShareAttributes = [
200
+            [
201
+                'scope' => 'permissions',
202
+                'key' => 'download',
203
+                'value' => true
204
+            ]
205
+        ];
206
+
207
+        $shareAttributes = $this->createMock(IShareAttributes::class);
208
+        $shareAttributes->method('toArray')->willReturn($formattedShareAttributes);
209
+        $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
210
+
211
+        // send both IShare attributes class and expected json string
212
+        return [$shareAttributes, \json_encode($formattedShareAttributes)];
213
+    }
214
+
215
+    public function testDeleteShareShareNotFound(): void {
216
+        $this->expectException(OCSNotFoundException::class);
217
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
218
+
219
+        $this->shareManager
220
+            ->expects($this->exactly(7))
221
+            ->method('getShareById')
222
+            ->willReturnCallback(function ($id): void {
223
+                if ($id === 'ocinternal:42' || $id === 'ocRoomShare:42' || $id === 'ocFederatedSharing:42' || $id === 'ocCircleShare:42' || $id === 'ocMailShare:42' || $id === 'deck:42' || $id === 'sciencemesh:42') {
224
+                    throw new ShareNotFound();
225
+                } else {
226
+                    throw new \Exception();
227
+                }
228
+            });
229
+
230
+        $this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
231
+
232
+        $this->ocs->deleteShare(42);
233
+    }
234
+
235
+    public function testDeleteShare(): void {
236
+        $node = $this->getMockBuilder(File::class)->getMock();
237
+
238
+        $share = $this->newShare();
239
+        $share->setSharedBy($this->currentUser)
240
+            ->setNode($node);
241
+        $this->shareManager
242
+            ->expects($this->once())
243
+            ->method('getShareById')
244
+            ->with('ocinternal:42')
245
+            ->willReturn($share);
246
+        $this->shareManager
247
+            ->expects($this->once())
248
+            ->method('deleteShare')
249
+            ->with($share);
250
+
251
+        $node->expects($this->once())
252
+            ->method('lock')
253
+            ->with(ILockingProvider::LOCK_SHARED);
254
+
255
+        $expected = new DataResponse();
256
+        $result = $this->ocs->deleteShare(42);
257
+
258
+        $this->assertInstanceOf(get_class($expected), $result);
259
+        $this->assertEquals($expected->getData(), $result->getData());
260
+    }
261
+
262
+
263
+    public function testDeleteShareLocked(): void {
264
+        $this->expectException(OCSNotFoundException::class);
265
+        $this->expectExceptionMessage('Could not delete share');
266
+
267
+        $node = $this->getMockBuilder(File::class)->getMock();
268
+        $node->method('getId')->willReturn(1);
269
+
270
+        $share = $this->newShare();
271
+        $share->setNode($node);
272
+
273
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
274
+        $this->rootFolder->method('getUserFolder')
275
+            ->with($this->currentUser)
276
+            ->willReturn($userFolder);
277
+
278
+        $userFolder->method('getById')
279
+            ->with($share->getNodeId())
280
+            ->willReturn([$node]);
281
+
282
+        $this->shareManager
283
+            ->expects($this->once())
284
+            ->method('getShareById')
285
+            ->with('ocinternal:42')
286
+            ->willReturn($share);
287
+
288
+        $this->shareManager
289
+            ->expects($this->never())
290
+            ->method('deleteShare')
291
+            ->with($share);
292
+
293
+        $node->expects($this->once())
294
+            ->method('lock')
295
+            ->with(ILockingProvider::LOCK_SHARED)
296
+            ->willThrowException(new LockedException('mypath'));
297
+
298
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
299
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
300
+
301
+        $this->ocs->deleteShare(42);
302
+    }
303
+
304
+    /**
305
+     * You can always remove a share that was shared with you
306
+     */
307
+    public function testDeleteShareWithMe(): void {
308
+        $node = $this->getMockBuilder(File::class)->getMock();
309
+
310
+        $share = $this->newShare();
311
+        $share->setSharedWith($this->currentUser)
312
+            ->setShareType(IShare::TYPE_USER)
313
+            ->setNode($node);
314
+
315
+        $this->shareManager
316
+            ->expects($this->once())
317
+            ->method('getShareById')
318
+            ->with('ocinternal:42')
319
+            ->willReturn($share);
320
+
321
+        $this->shareManager
322
+            ->expects($this->once())
323
+            ->method('deleteShare')
324
+            ->with($share);
325
+
326
+        $node->expects($this->once())
327
+            ->method('lock')
328
+            ->with(ILockingProvider::LOCK_SHARED);
329
+
330
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
331
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
332
+
333
+        $this->ocs->deleteShare(42);
334
+    }
335
+
336
+    /**
337
+     * You can always delete a share you own
338
+     */
339
+    public function testDeleteShareOwner(): void {
340
+        $node = $this->getMockBuilder(File::class)->getMock();
341
+
342
+        $share = $this->newShare();
343
+        $share->setSharedBy($this->currentUser)
344
+            ->setNode($node);
345
+
346
+        $this->shareManager
347
+            ->expects($this->once())
348
+            ->method('getShareById')
349
+            ->with('ocinternal:42')
350
+            ->willReturn($share);
351
+
352
+        $this->shareManager
353
+            ->expects($this->once())
354
+            ->method('deleteShare')
355
+            ->with($share);
356
+
357
+        $node->expects($this->once())
358
+            ->method('lock')
359
+            ->with(ILockingProvider::LOCK_SHARED);
360
+
361
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
362
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
363
+
364
+        $this->ocs->deleteShare(42);
365
+    }
366
+
367
+    /**
368
+     * You can always delete a share when you own
369
+     * the file path it belong to
370
+     */
371
+    public function testDeleteShareFileOwner(): void {
372
+        $node = $this->getMockBuilder(File::class)->getMock();
373
+        $node->method('getId')->willReturn(1);
374
+
375
+        $share = $this->newShare();
376
+        $share->setShareOwner($this->currentUser)
377
+            ->setNode($node);
378
+
379
+        $this->shareManager
380
+            ->expects($this->once())
381
+            ->method('getShareById')
382
+            ->with('ocinternal:42')
383
+            ->willReturn($share);
384
+
385
+        $this->shareManager
386
+            ->expects($this->once())
387
+            ->method('deleteShare')
388
+            ->with($share);
389
+
390
+        $node->expects($this->once())
391
+            ->method('lock')
392
+            ->with(ILockingProvider::LOCK_SHARED);
393
+
394
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteFromSelf', [$share]));
395
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
396
+
397
+        $this->ocs->deleteShare(42);
398
+    }
399
+
400
+    /**
401
+     * You can remove (the mountpoint, not the share)
402
+     * a share if you're in the group the share is shared with
403
+     */
404
+    public function testDeleteSharedWithMyGroup(): void {
405
+        $node = $this->getMockBuilder(File::class)->getMock();
406
+        $node->method('getId')->willReturn(1);
407
+
408
+        $share = $this->newShare();
409
+        $share->setShareType(IShare::TYPE_GROUP)
410
+            ->setSharedWith('group')
411
+            ->setNode($node);
412
+
413
+        $this->shareManager
414
+            ->expects($this->once())
415
+            ->method('getShareById')
416
+            ->with('ocinternal:42')
417
+            ->willReturn($share);
418
+
419
+        // canDeleteShareFromSelf
420
+        $user = $this->createMock(IUser::class);
421
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
422
+        $this->groupManager
423
+            ->method('get')
424
+            ->with('group')
425
+            ->willReturn($group);
426
+        $this->userManager
427
+            ->method('get')
428
+            ->with($this->currentUser)
429
+            ->willReturn($user);
430
+        $group->method('inGroup')
431
+            ->with($user)
432
+            ->willReturn(true);
433
+
434
+        $node->expects($this->once())
435
+            ->method('lock')
436
+            ->with(ILockingProvider::LOCK_SHARED);
437
+
438
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
439
+        $this->rootFolder->method('getUserFolder')
440
+            ->with($this->currentUser)
441
+            ->willReturn($userFolder);
442
+
443
+        $userFolder->method('getById')
444
+            ->with($share->getNodeId())
445
+            ->willReturn([$share->getNode()]);
446
+
447
+        $this->shareManager->expects($this->once())
448
+            ->method('deleteFromSelf')
449
+            ->with($share, $this->currentUser);
450
+
451
+        $this->shareManager->expects($this->never())
452
+            ->method('deleteShare');
453
+
454
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
455
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
456
+
457
+        $this->ocs->deleteShare(42);
458
+    }
459
+
460
+    /**
461
+     * You cannot remove a share if you're not
462
+     * in the group the share is shared with
463
+     */
464
+    public function testDeleteSharedWithGroupIDontBelongTo(): void {
465
+        $this->expectException(OCSNotFoundException::class);
466
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
467
+
468
+        $node = $this->getMockBuilder(File::class)->getMock();
469
+        $node->method('getId')->willReturn(42);
470
+
471
+        $share = $this->newShare();
472
+        $share->setShareType(IShare::TYPE_GROUP)
473
+            ->setSharedWith('group')
474
+            ->setNode($node);
475
+
476
+        $this->shareManager
477
+            ->expects($this->once())
478
+            ->method('getShareById')
479
+            ->with('ocinternal:42')
480
+            ->willReturn($share);
481
+
482
+        // canDeleteShareFromSelf
483
+        $user = $this->createMock(IUser::class);
484
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
485
+        $this->groupManager
486
+            ->method('get')
487
+            ->with('group')
488
+            ->willReturn($group);
489
+        $this->userManager
490
+            ->method('get')
491
+            ->with($this->currentUser)
492
+            ->willReturn($user);
493
+        $group->method('inGroup')
494
+            ->with($user)
495
+            ->willReturn(false);
496
+
497
+        $node->expects($this->once())
498
+            ->method('lock')
499
+            ->with(ILockingProvider::LOCK_SHARED);
500
+
501
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
502
+        $this->rootFolder->method('getUserFolder')
503
+            ->with($this->currentUser)
504
+            ->willReturn($userFolder);
505
+
506
+        $userFolder->method('getById')
507
+            ->with($share->getNodeId())
508
+            ->willReturn([$share->getNode()]);
509
+
510
+        $this->shareManager->expects($this->never())
511
+            ->method('deleteFromSelf');
512
+
513
+        $this->shareManager->expects($this->never())
514
+            ->method('deleteShare');
515
+
516
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShareFromSelf', [$share]));
517
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canDeleteShare', [$share]));
518
+
519
+        $this->ocs->deleteShare(42);
520
+    }
521
+
522
+    public function testDeleteShareOwnerless(): void {
523
+        $ocs = $this->mockFormatShare();
524
+
525
+        $mount = $this->createMock(IShareOwnerlessMount::class);
526
+
527
+        $file = $this->createMock(File::class);
528
+        $file
529
+            ->expects($this->exactly(2))
530
+            ->method('getPermissions')
531
+            ->willReturn(Constants::PERMISSION_SHARE);
532
+        $file
533
+            ->expects($this->once())
534
+            ->method('getMountPoint')
535
+            ->willReturn($mount);
536
+
537
+        $userFolder = $this->createMock(Folder::class);
538
+        $userFolder->method('getById')
539
+            ->with(2)
540
+            ->willReturn([$file]);
541
+        $userFolder->method('getFirstNodeById')
542
+            ->with(2)
543
+            ->willReturn($file);
544
+
545
+        $this->rootFolder
546
+            ->method('getUserFolder')
547
+            ->with($this->currentUser)
548
+            ->willReturn($userFolder);
549
+
550
+        $share = $this->createMock(IShare::class);
551
+        $share
552
+            ->expects($this->once())
553
+            ->method('getNode')
554
+            ->willReturn($file);
555
+        $share
556
+            ->expects($this->exactly(2))
557
+            ->method('getNodeId')
558
+            ->willReturn(2);
559
+        $share
560
+            ->expects($this->exactly(2))
561
+            ->method('getPermissions')
562
+            ->willReturn(Constants::PERMISSION_SHARE);
563
+
564
+        $this->shareManager
565
+            ->expects($this->once())
566
+            ->method('getShareById')
567
+            ->with('ocinternal:1', $this->currentUser)
568
+            ->willReturn($share);
569
+
570
+        $this->shareManager
571
+            ->expects($this->once())
572
+            ->method('deleteShare')
573
+            ->with($share);
574
+
575
+        $result = $ocs->deleteShare(1);
576
+        $this->assertInstanceOf(DataResponse::class, $result);
577
+    }
578
+
579
+    /*
580 580
 	 * FIXME: Enable once we have a federated Share Provider
581 581
 
582 582
 	public function testGetGetShareNotExists() {
@@ -591,5061 +591,5061 @@  discard block
 block discarded – undo
591 591
 	}
592 592
 	*/
593 593
 
594
-	public function createShare(
595
-		int $id,
596
-		int $shareType,
597
-		?string $sharedWith,
598
-		string $sharedBy,
599
-		string $shareOwner,
600
-		File|Folder|null $node,
601
-		int $permissions,
602
-		int $shareTime,
603
-		?\DateTime $expiration,
604
-		int $parent,
605
-		string $target,
606
-		int $mail_send,
607
-		string $note = '',
608
-		?string $token = null,
609
-		?string $password = null,
610
-		string $label = '',
611
-		?IShareAttributes $attributes = null,
612
-	): MockObject {
613
-		$share = $this->createMock(IShare::class);
614
-		$share->method('getId')->willReturn($id);
615
-		$share->method('getShareType')->willReturn($shareType);
616
-		$share->method('getSharedWith')->willReturn($sharedWith);
617
-		$share->method('getSharedBy')->willReturn($sharedBy);
618
-		$share->method('getShareOwner')->willReturn($shareOwner);
619
-		$share->method('getNode')->willReturn($node);
620
-		$share->method('getPermissions')->willReturn($permissions);
621
-		$share->method('getNote')->willReturn($note);
622
-		$share->method('getLabel')->willReturn($label);
623
-		$share->method('getAttributes')->willReturn($attributes);
624
-		$time = new \DateTime();
625
-		$time->setTimestamp($shareTime);
626
-		$share->method('getShareTime')->willReturn($time);
627
-		$share->method('getExpirationDate')->willReturn($expiration);
628
-		$share->method('getTarget')->willReturn($target);
629
-		$share->method('getMailSend')->willReturn($mail_send);
630
-		$share->method('getToken')->willReturn($token);
631
-		$share->method('getPassword')->willReturn($password);
632
-
633
-		if ($shareType === IShare::TYPE_USER
634
-			|| $shareType === IShare::TYPE_GROUP
635
-			|| $shareType === IShare::TYPE_LINK) {
636
-			$share->method('getFullId')->willReturn('ocinternal:' . $id);
637
-		}
638
-
639
-		return $share;
640
-	}
641
-
642
-	public static function dataGetShare(): array {
643
-		$data = [];
644
-
645
-		$file = [
646
-			'class' => File::class,
647
-			'id' => 1,
648
-			'path' => 'file',
649
-			'mimeType' => 'myMimeType',
650
-		];
651
-
652
-		$folder = [
653
-			'class' => Folder::class,
654
-			'id' => 2,
655
-			'path' => 'folder',
656
-			'mimeType' => 'myFolderMimeType',
657
-		];
658
-
659
-		// File shared with user
660
-		$share = [
661
-			100,
662
-			IShare::TYPE_USER,
663
-			'userId',
664
-			'initiatorId',
665
-			'ownerId',
666
-			$file,
667
-			4,
668
-			5,
669
-			null,
670
-			6,
671
-			'target',
672
-			0,
673
-			'personal note',
674
-			null,
675
-			null,
676
-			'',
677
-			[],
678
-		];
679
-		$expected = [
680
-			'id' => 100,
681
-			'share_type' => IShare::TYPE_USER,
682
-			'share_with' => 'userId',
683
-			'share_with_displayname' => 'userDisplay',
684
-			'share_with_displayname_unique' => '[email protected]',
685
-			'uid_owner' => 'initiatorId',
686
-			'displayname_owner' => 'initiatorDisplay',
687
-			'item_type' => 'file',
688
-			'item_source' => 1,
689
-			'file_source' => 1,
690
-			'file_target' => 'target',
691
-			'file_parent' => 3,
692
-			'token' => null,
693
-			'expiration' => null,
694
-			'permissions' => 4,
695
-			'stime' => 5,
696
-			'parent' => null,
697
-			'storage_id' => 'STORAGE',
698
-			'path' => 'file',
699
-			'storage' => 101,
700
-			'mail_send' => 0,
701
-			'uid_file_owner' => 'ownerId',
702
-			'note' => 'personal note',
703
-			'label' => '',
704
-			'displayname_file_owner' => 'ownerDisplay',
705
-			'mimetype' => 'myMimeType',
706
-			'has_preview' => false,
707
-			'hide_download' => 0,
708
-			'can_edit' => false,
709
-			'can_delete' => false,
710
-			'item_size' => 123465,
711
-			'item_mtime' => 1234567890,
712
-			'item_permissions' => 4,
713
-			'is-mount-root' => false,
714
-			'mount-type' => '',
715
-		];
716
-		$data['File shared with user'] = [$share, $expected, true];
717
-
718
-		// Folder shared with group
719
-		$share = [
720
-			101,
721
-			IShare::TYPE_GROUP,
722
-			'groupId',
723
-			'initiatorId',
724
-			'ownerId',
725
-			$folder,
726
-			4,
727
-			5,
728
-			null,
729
-			6,
730
-			'target',
731
-			0,
732
-			'personal note',
733
-			null,
734
-			null,
735
-			'',
736
-			[],
737
-		];
738
-		$expected = [
739
-			'id' => 101,
740
-			'share_type' => IShare::TYPE_GROUP,
741
-			'share_with' => 'groupId',
742
-			'share_with_displayname' => 'groupId',
743
-			'uid_owner' => 'initiatorId',
744
-			'displayname_owner' => 'initiatorDisplay',
745
-			'item_type' => 'folder',
746
-			'item_source' => 2,
747
-			'file_source' => 2,
748
-			'file_target' => 'target',
749
-			'file_parent' => 3,
750
-			'token' => null,
751
-			'expiration' => null,
752
-			'permissions' => 4,
753
-			'stime' => 5,
754
-			'parent' => null,
755
-			'storage_id' => 'STORAGE',
756
-			'path' => 'folder',
757
-			'storage' => 101,
758
-			'mail_send' => 0,
759
-			'uid_file_owner' => 'ownerId',
760
-			'note' => 'personal note',
761
-			'label' => '',
762
-			'displayname_file_owner' => 'ownerDisplay',
763
-			'mimetype' => 'myFolderMimeType',
764
-			'has_preview' => false,
765
-			'hide_download' => 0,
766
-			'can_edit' => false,
767
-			'can_delete' => false,
768
-			'item_size' => 123465,
769
-			'item_mtime' => 1234567890,
770
-			'item_permissions' => 4,
771
-			'is-mount-root' => false,
772
-			'mount-type' => '',
773
-		];
774
-		$data['Folder shared with group'] = [$share, $expected, true];
775
-
776
-		// File shared by link with Expire
777
-		$expire = \DateTime::createFromFormat('Y-m-d h:i:s', '2000-01-02 01:02:03');
778
-		$share = [
779
-			101,
780
-			IShare::TYPE_LINK,
781
-			null,
782
-			'initiatorId',
783
-			'ownerId',
784
-			$folder,
785
-			4,
786
-			5,
787
-			$expire,
788
-			6,
789
-			'target',
790
-			0,
791
-			'personal note',
792
-			'token',
793
-			'password',
794
-			'first link share'
795
-		];
796
-		$expected = [
797
-			'id' => 101,
798
-			'share_type' => IShare::TYPE_LINK,
799
-			'password' => 'password',
800
-			'share_with' => 'password',
801
-			'share_with_displayname' => '(Shared link)',
802
-			'send_password_by_talk' => false,
803
-			'uid_owner' => 'initiatorId',
804
-			'displayname_owner' => 'initiatorDisplay',
805
-			'item_type' => 'folder',
806
-			'item_source' => 2,
807
-			'file_source' => 2,
808
-			'file_target' => 'target',
809
-			'file_parent' => 3,
810
-			'token' => 'token',
811
-			'expiration' => '2000-01-02 00:00:00',
812
-			'permissions' => 4,
813
-			'attributes' => null,
814
-			'stime' => 5,
815
-			'parent' => null,
816
-			'storage_id' => 'STORAGE',
817
-			'path' => 'folder',
818
-			'storage' => 101,
819
-			'mail_send' => 0,
820
-			'url' => 'url',
821
-			'uid_file_owner' => 'ownerId',
822
-			'note' => 'personal note',
823
-			'label' => 'first link share',
824
-			'displayname_file_owner' => 'ownerDisplay',
825
-			'mimetype' => 'myFolderMimeType',
826
-			'has_preview' => false,
827
-			'hide_download' => 0,
828
-			'can_edit' => false,
829
-			'can_delete' => false,
830
-			'item_size' => 123465,
831
-			'item_mtime' => 1234567890,
832
-			'item_permissions' => 4,
833
-			'is-mount-root' => false,
834
-			'mount-type' => '',
835
-		];
836
-		$data['File shared by link with Expire'] = [$share, $expected, false];
837
-
838
-		return $data;
839
-	}
840
-
841
-	#[DataProvider('dataGetShare')]
842
-	public function testGetShare(array $shareParams, array $result, bool $attributes): void {
843
-
844
-		$cache = $this->createMock(ICache::class);
845
-		$cache->method('getNumericStorageId')->willReturn(101);
846
-
847
-		$storage = $this->createMock(IStorage::class);
848
-		$storage->method('getId')->willReturn('STORAGE');
849
-		$storage->method('getCache')->willReturn($cache);
850
-
851
-		$parentFolder = $this->createMock(Folder::class);
852
-		$parentFolder->method('getId')->willReturn(3);
853
-
854
-		$mountPoint = $this->createMock(IMountPoint::class);
855
-		$mountPoint->method('getMountType')->willReturn('');
856
-
857
-		$nodeParams = $shareParams[5];
858
-		$node = $this->createMock($nodeParams['class']);
859
-		$node->method('getId')->willReturn($nodeParams['id']);
860
-		$node->method('getPath')->willReturn($nodeParams['path']);
861
-		$node->method('getStorage')->willReturn($storage);
862
-		$node->method('getParent')->willReturn($parentFolder);
863
-		$node->method('getSize')->willReturn(123465);
864
-		$node->method('getMTime')->willReturn(1234567890);
865
-		$node->method('getMimeType')->willReturn($nodeParams['mimeType']);
866
-		$node->method('getMountPoint')->willReturn($mountPoint);
867
-
868
-		$shareParams[5] = $node;
869
-
870
-		if ($attributes) {
871
-			[$shareAttributes, $shareAttributesReturnJson] = $this->mockShareAttributes();
872
-			$result['attributes'] = $shareAttributesReturnJson;
873
-			$shareParams[16] = $shareAttributes;
874
-		}
875
-
876
-		$share = $this->createShare(...$shareParams);
877
-		/** @var ShareAPIController&MockObject $ocs */
878
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
879
-			->setConstructorArgs([
880
-				$this->appName,
881
-				$this->request,
882
-				$this->shareManager,
883
-				$this->groupManager,
884
-				$this->userManager,
885
-				$this->rootFolder,
886
-				$this->urlGenerator,
887
-				$this->l,
888
-				$this->config,
889
-				$this->appConfig,
890
-				$this->appManager,
891
-				$this->serverContainer,
892
-				$this->userStatusManager,
893
-				$this->previewManager,
894
-				$this->dateTimeZone,
895
-				$this->logger,
896
-				$this->factory,
897
-				$this->mailer,
898
-				$this->tagManager,
899
-				$this->getEmailValidatorWithStrictEmailCheck(),
900
-				$this->trustedServers,
901
-				$this->currentUser,
902
-			])
903
-			->onlyMethods(['canAccessShare'])
904
-			->getMock();
905
-
906
-		$ocs->expects($this->any())
907
-			->method('canAccessShare')
908
-			->willReturn(true);
909
-
910
-		$this->shareManager
911
-			->expects($this->any())
912
-			->method('getShareById')
913
-			->with($share->getFullId(), 'currentUser')
914
-			->willReturn($share);
915
-
916
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
917
-		$userFolder
918
-			->method('getRelativePath')
919
-			->willReturnArgument(0);
920
-
921
-		$userFolder->method('getById')
922
-			->with($share->getNodeId())
923
-			->willReturn([$share->getNode()]);
924
-		$userFolder->method('getFirstNodeById')
925
-			->with($share->getNodeId())
926
-			->willReturn($share->getNode());
927
-
928
-		$this->rootFolder->method('getUserFolder')
929
-			->with($this->currentUser)
930
-			->willReturn($userFolder);
931
-
932
-		$this->urlGenerator
933
-			->method('linkToRouteAbsolute')
934
-			->willReturn('url');
935
-
936
-		$initiator = $this->getMockBuilder(IUser::class)->getMock();
937
-		$initiator->method('getUID')->willReturn('initiatorId');
938
-		$initiator->method('getDisplayName')->willReturn('initiatorDisplay');
939
-
940
-		$owner = $this->getMockBuilder(IUser::class)->getMock();
941
-		$owner->method('getUID')->willReturn('ownerId');
942
-		$owner->method('getDisplayName')->willReturn('ownerDisplay');
943
-
944
-		$user = $this->getMockBuilder(IUser::class)->getMock();
945
-		$user->method('getUID')->willReturn('userId');
946
-		$user->method('getDisplayName')->willReturn('userDisplay');
947
-		$user->method('getSystemEMailAddress')->willReturn('[email protected]');
948
-
949
-		$group = $this->getMockBuilder(IGroup::class)->getMock();
950
-		$group->method('getGID')->willReturn('groupId');
951
-
952
-		$this->userManager->method('get')->willReturnMap([
953
-			['userId', $user],
954
-			['initiatorId', $initiator],
955
-			['ownerId', $owner],
956
-		]);
957
-		$this->groupManager->method('get')->willReturnMap([
958
-			['group', $group],
959
-		]);
960
-		$this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
961
-
962
-		$data = $ocs->getShare((string)$share->getId())->getData()[0];
963
-		$this->assertEquals($result, $data);
964
-	}
965
-
966
-
967
-	public function testGetShareInvalidNode(): void {
968
-		$this->expectException(OCSNotFoundException::class);
969
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
970
-
971
-		$share = Server::get(IManager::class)->newShare();
972
-		$share->setSharedBy('initiator')
973
-			->setSharedWith('recipient')
974
-			->setShareOwner('owner');
975
-
976
-		$this->shareManager
977
-			->expects($this->once())
978
-			->method('getShareById')
979
-			->with('ocinternal:42', 'currentUser')
980
-			->willReturn($share);
981
-
982
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
983
-		$this->rootFolder->method('getUserFolder')
984
-			->with($this->currentUser)
985
-			->willReturn($userFolder);
986
-
987
-		$this->ocs->getShare('42');
988
-	}
989
-
990
-	public static function dataGetShares(): array {
991
-		$file1 = [
992
-			'class' => File::class,
993
-			'methods' => [
994
-				'getName' => 'file1',
995
-			]
996
-		];
997
-		$file2 = [
998
-			'class' => File::class,
999
-			'methods' => [
1000
-				'getName' => 'file2',
1001
-			]
1002
-		];
1003
-
1004
-		$folder = [
1005
-			'class' => Folder::class,
1006
-			'methods' => [
1007
-				'getDirectoryListing' => [$file1, $file2]
1008
-			]
1009
-		];
1010
-
1011
-		$file1UserShareOwner = [
1012
-			'type' => IShare::TYPE_USER,
1013
-			'sharedWith' => 'recipient',
1014
-			'sharedBy' => 'initiator',
1015
-			'owner' => 'currentUser',
1016
-			'node' => $file1,
1017
-			'id' => 4,
1018
-		];
1019
-
1020
-		$file1UserShareOwnerExpected = [
1021
-			'id' => 4,
1022
-			'share_type' => IShare::TYPE_USER,
1023
-		];
1024
-
1025
-		$file1UserShareInitiator = [
1026
-			'type' => IShare::TYPE_USER,
1027
-			'sharedWith' => 'recipient',
1028
-			'sharedBy' => 'currentUser',
1029
-			'owner' => 'owner',
1030
-			'node' => $file1,
1031
-			'id' => 8,
1032
-		];
1033
-
1034
-		$file1UserShareInitiatorExpected = [
1035
-			'id' => 8,
1036
-			'share_type' => IShare::TYPE_USER,
1037
-		];
1038
-
1039
-		$file1UserShareRecipient = [
1040
-			'type' => IShare::TYPE_USER,
1041
-			'sharedWith' => 'currentUser',
1042
-			'sharedBy' => 'initiator',
1043
-			'owner' => 'owner',
1044
-			'node' => $file1,
1045
-			'id' => 15,
1046
-		];
1047
-
1048
-		$file1UserShareRecipientExpected = [
1049
-			'id' => 15,
1050
-			'share_type' => IShare::TYPE_USER,
1051
-		];
1052
-
1053
-		$file1UserShareOther = [
1054
-			'type' => IShare::TYPE_USER,
1055
-			'sharedWith' => 'recipient',
1056
-			'sharedBy' => 'initiator',
1057
-			'owner' => 'owner',
1058
-			'node' => $file1,
1059
-			'id' => 16,
1060
-		];
1061
-
1062
-		$file1UserShareOtherExpected = [
1063
-			'id' => 16,
1064
-			'share_type' => IShare::TYPE_USER,
1065
-		];
1066
-
1067
-		$file1GroupShareOwner = [
1068
-			'type' => IShare::TYPE_GROUP,
1069
-			'sharedWith' => 'recipient',
1070
-			'sharedBy' => 'initiator',
1071
-			'owner' => 'currentUser',
1072
-			'node' => $file1,
1073
-			'id' => 23,
1074
-		];
1075
-
1076
-		$file1GroupShareOwnerExpected = [
1077
-			'id' => 23,
1078
-			'share_type' => IShare::TYPE_GROUP,
1079
-		];
1080
-
1081
-		$file1GroupShareRecipient = [
1082
-			'type' => IShare::TYPE_GROUP,
1083
-			'sharedWith' => 'currentUserGroup',
1084
-			'sharedBy' => 'initiator',
1085
-			'owner' => 'owner',
1086
-			'node' => $file1,
1087
-			'id' => 42,
1088
-		];
1089
-
1090
-		$file1GroupShareRecipientExpected = [
1091
-			'id' => 42,
1092
-			'share_type' => IShare::TYPE_GROUP,
1093
-		];
1094
-
1095
-		$file1GroupShareOther = [
1096
-			'type' => IShare::TYPE_GROUP,
1097
-			'sharedWith' => 'recipient',
1098
-			'sharedBy' => 'initiator',
1099
-			'owner' => 'owner',
1100
-			'node' => $file1,
1101
-			'id' => 108,
1102
-		];
1103
-
1104
-		$file1LinkShareOwner = [
1105
-			'type' => IShare::TYPE_LINK,
1106
-			'sharedWith' => 'recipient',
1107
-			'sharedBy' => 'initiator',
1108
-			'owner' => 'currentUser',
1109
-			'node' => $file1,
1110
-			'id' => 415,
1111
-		];
1112
-
1113
-		$file1LinkShareOwnerExpected = [
1114
-			'id' => 415,
1115
-			'share_type' => IShare::TYPE_LINK,
1116
-		];
1117
-
1118
-		$file1EmailShareOwner = [
1119
-			'type' => IShare::TYPE_EMAIL,
1120
-			'sharedWith' => 'recipient',
1121
-			'sharedBy' => 'initiator',
1122
-			'owner' => 'currentUser',
1123
-			'node' => $file1,
1124
-			'id' => 416,
1125
-		];
1126
-
1127
-		$file1EmailShareOwnerExpected = [
1128
-			'id' => 416,
1129
-			'share_type' => IShare::TYPE_EMAIL,
1130
-		];
1131
-
1132
-		$file1CircleShareOwner = [
1133
-			'type' => IShare::TYPE_CIRCLE,
1134
-			'sharedWith' => 'recipient',
1135
-			'sharedBy' => 'initiator',
1136
-			'owner' => 'currentUser',
1137
-			'node' => $file1,
1138
-			'id' => 423,
1139
-		];
1140
-
1141
-		$file1CircleShareOwnerExpected = [
1142
-			'id' => 423,
1143
-			'share_type' => IShare::TYPE_CIRCLE,
1144
-		];
1145
-
1146
-		$file1RoomShareOwner = [
1147
-			'type' => IShare::TYPE_ROOM,
1148
-			'sharedWith' => 'recipient',
1149
-			'sharedBy' => 'initiator',
1150
-			'owner' => 'currentUser',
1151
-			'node' => $file1,
1152
-			'id' => 442,
1153
-		];
1154
-
1155
-		$file1RoomShareOwnerExpected = [
1156
-			'id' => 442,
1157
-			'share_type' => IShare::TYPE_ROOM,
1158
-		];
1159
-
1160
-		$file1RemoteShareOwner = [
1161
-			'type' => IShare::TYPE_REMOTE,
1162
-			'sharedWith' => 'recipient',
1163
-			'sharedBy' => 'initiator',
1164
-			'owner' => 'currentUser',
1165
-			'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1166
-			'node' => $file1,
1167
-			'id' => 815,
1168
-		];
1169
-
1170
-		$file1RemoteShareOwnerExpected = [
1171
-			'id' => 815,
1172
-			'share_type' => IShare::TYPE_REMOTE,
1173
-		];
1174
-
1175
-		$file1RemoteGroupShareOwner = [
1176
-			'type' => IShare::TYPE_REMOTE_GROUP,
1177
-			'sharedWith' => 'recipient',
1178
-			'sharedBy' => 'initiator',
1179
-			'owner' => 'currentUser',
1180
-			'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1181
-			'node' => $file1,
1182
-			'id' => 816,
1183
-		];
1184
-
1185
-		$file1RemoteGroupShareOwnerExpected = [
1186
-			'id' => 816,
1187
-			'share_type' => IShare::TYPE_REMOTE_GROUP,
1188
-		];
1189
-
1190
-		$file2UserShareOwner = [
1191
-			'type' => IShare::TYPE_USER,
1192
-			'sharedWith' => 'recipient',
1193
-			'sharedBy' => 'initiator',
1194
-			'owner' => 'currentUser',
1195
-			'node' => $file2,
1196
-			'id' => 823,
1197
-		];
1198
-
1199
-		$file2UserShareOwnerExpected = [
1200
-			'id' => 823,
1201
-			'share_type' => IShare::TYPE_USER,
1202
-		];
1203
-
1204
-		$data = [
1205
-			[
1206
-				[
1207
-					'node' => $file1,
1208
-				],
1209
-				[
1210
-					'file1' => [
1211
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1212
-					],
1213
-				],
1214
-				[
1215
-				],
1216
-				[
1217
-					$file1UserShareOwnerExpected
1218
-				]
1219
-			],
1220
-			[
1221
-				[
1222
-					'node' => $file1,
1223
-				],
1224
-				[
1225
-					'file1' => [
1226
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1227
-					],
1228
-				],
1229
-				[
1230
-				],
1231
-				[
1232
-					$file1UserShareOwnerExpected,
1233
-				]
1234
-			],
1235
-			[
1236
-				[
1237
-					'node' => $file1,
1238
-				],
1239
-				[
1240
-					'file1' => [
1241
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1242
-					],
1243
-				],
1244
-				[
1245
-				],
1246
-				[
1247
-					$file1UserShareOwnerExpected,
1248
-					$file1UserShareInitiatorExpected,
1249
-					$file1UserShareOtherExpected,
1250
-				]
1251
-			],
1252
-			[
1253
-				[
1254
-					'node' => $file1,
1255
-				],
1256
-				[
1257
-					'file1' => [
1258
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1259
-					],
1260
-				],
1261
-				[
1262
-				],
1263
-				[
1264
-					$file1UserShareInitiatorExpected,
1265
-				]
1266
-			],
1267
-			[
1268
-				[
1269
-					'node' => $file1,
1270
-				],
1271
-				[
1272
-					'file1' => [
1273
-						IShare::TYPE_USER => [$file1UserShareOwner],
1274
-						IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1275
-					],
1276
-				],
1277
-				[
1278
-				],
1279
-				[
1280
-					$file1UserShareOwnerExpected,
1281
-					$file1GroupShareRecipientExpected,
1282
-				]
1283
-			],
1284
-			[
1285
-				[
1286
-					'node' => $file1,
1287
-				],
1288
-				[
1289
-					'file1' => [
1290
-						IShare::TYPE_USER => [$file1UserShareOwner],
1291
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1292
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1293
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1294
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1295
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1296
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1297
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1298
-					],
1299
-				],
1300
-				[
1301
-				],
1302
-				[
1303
-					$file1UserShareOwnerExpected,
1304
-					$file1GroupShareOwnerExpected,
1305
-					$file1LinkShareOwnerExpected,
1306
-					$file1EmailShareOwnerExpected,
1307
-					$file1CircleShareOwnerExpected,
1308
-					$file1RoomShareOwnerExpected,
1309
-				]
1310
-			],
1311
-			[
1312
-				[
1313
-					'node' => $file1,
1314
-				],
1315
-				[
1316
-					'file1' => [
1317
-						IShare::TYPE_USER => [$file1UserShareOwner],
1318
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1319
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1320
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1321
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1322
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1323
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1324
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1325
-					],
1326
-				],
1327
-				[
1328
-					IShare::TYPE_REMOTE => true,
1329
-					IShare::TYPE_REMOTE_GROUP => true,
1330
-				],
1331
-				[
1332
-					$file1UserShareOwnerExpected,
1333
-					$file1GroupShareOwnerExpected,
1334
-					$file1LinkShareOwnerExpected,
1335
-					$file1EmailShareOwnerExpected,
1336
-					$file1CircleShareOwnerExpected,
1337
-					$file1RoomShareOwnerExpected,
1338
-					$file1RemoteShareOwnerExpected,
1339
-					$file1RemoteGroupShareOwnerExpected,
1340
-				]
1341
-			],
1342
-			[
1343
-				[
1344
-					'node' => $folder,
1345
-					'subfiles' => 'true',
1346
-				],
1347
-				[
1348
-					'file1' => [
1349
-						IShare::TYPE_USER => [$file1UserShareOwner],
1350
-					],
1351
-					'file2' => [
1352
-						IShare::TYPE_USER => [$file2UserShareOwner],
1353
-					],
1354
-				],
1355
-				[
1356
-				],
1357
-				[
1358
-					$file1UserShareOwnerExpected,
1359
-					$file2UserShareOwnerExpected,
1360
-				]
1361
-			],
1362
-			[
1363
-				[
1364
-					'node' => $folder,
1365
-					'subfiles' => 'true',
1366
-				],
1367
-				[
1368
-					'file1' => [
1369
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1370
-					],
1371
-				],
1372
-				[
1373
-				],
1374
-				[
1375
-					$file1UserShareOwnerExpected,
1376
-				]
1377
-			],
1378
-			[
1379
-				[
1380
-					'node' => $folder,
1381
-					'subfiles' => 'true',
1382
-				],
1383
-				[
1384
-					'file1' => [
1385
-						IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1386
-					],
1387
-				],
1388
-				[
1389
-				],
1390
-				[
1391
-					$file1UserShareOwnerExpected
1392
-				]
1393
-			],
1394
-			[
1395
-				[
1396
-					'node' => $folder,
1397
-					'subfiles' => 'true',
1398
-				],
1399
-				[
1400
-					'file1' => [
1401
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1402
-					],
1403
-					'file2' => [
1404
-						IShare::TYPE_USER => [$file2UserShareOwner],
1405
-					],
1406
-				],
1407
-				[
1408
-				],
1409
-				[
1410
-					$file1UserShareInitiatorExpected,
1411
-					$file1UserShareOtherExpected,
1412
-					$file2UserShareOwnerExpected,
1413
-				]
1414
-			],
1415
-			// This might not happen in a real environment, as the combination
1416
-			// of shares does not seem to be possible on a folder without
1417
-			// resharing rights; if the folder has resharing rights then the
1418
-			// share with others would be included too in the results.
1419
-			[
1420
-				[
1421
-					'node' => $folder,
1422
-					'subfiles' => 'true',
1423
-				],
1424
-				[
1425
-					'file1' => [
1426
-						IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1427
-					],
1428
-				],
1429
-				[
1430
-				],
1431
-				[
1432
-					$file1UserShareInitiatorExpected,
1433
-				]
1434
-			],
1435
-			[
1436
-				[
1437
-					'node' => $folder,
1438
-					'subfiles' => 'true',
1439
-				],
1440
-				[
1441
-					'file1' => [
1442
-						IShare::TYPE_USER => [$file1UserShareOwner],
1443
-						IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1444
-					],
1445
-				],
1446
-				[
1447
-				],
1448
-				[
1449
-					$file1UserShareOwnerExpected,
1450
-					$file1GroupShareRecipientExpected,
1451
-				]
1452
-			],
1453
-			[
1454
-				[
1455
-					'node' => $folder,
1456
-					'subfiles' => 'true',
1457
-				],
1458
-				[
1459
-					'file1' => [
1460
-						IShare::TYPE_USER => [$file1UserShareOwner],
1461
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1462
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1463
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1464
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1465
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1466
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1467
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1468
-					],
1469
-				],
1470
-				[
1471
-				],
1472
-				[
1473
-					$file1UserShareOwnerExpected,
1474
-					$file1GroupShareOwnerExpected,
1475
-					$file1LinkShareOwnerExpected,
1476
-					$file1EmailShareOwnerExpected,
1477
-					$file1CircleShareOwnerExpected,
1478
-					$file1RoomShareOwnerExpected,
1479
-				]
1480
-			],
1481
-			[
1482
-				[
1483
-					'node' => $folder,
1484
-					'subfiles' => 'true',
1485
-				],
1486
-				[
1487
-					'file1' => [
1488
-						IShare::TYPE_USER => [$file1UserShareOwner],
1489
-						IShare::TYPE_GROUP => [$file1GroupShareOwner],
1490
-						IShare::TYPE_LINK => [$file1LinkShareOwner],
1491
-						IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1492
-						IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1493
-						IShare::TYPE_ROOM => [$file1RoomShareOwner],
1494
-						IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1495
-						IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1496
-					],
1497
-				],
1498
-				[
1499
-					IShare::TYPE_REMOTE => true,
1500
-					IShare::TYPE_REMOTE_GROUP => true,
1501
-				],
1502
-				[
1503
-					$file1UserShareOwnerExpected,
1504
-					$file1GroupShareOwnerExpected,
1505
-					$file1LinkShareOwnerExpected,
1506
-					$file1EmailShareOwnerExpected,
1507
-					$file1CircleShareOwnerExpected,
1508
-					$file1RoomShareOwnerExpected,
1509
-					$file1RemoteShareOwnerExpected,
1510
-					$file1RemoteGroupShareOwnerExpected,
1511
-				]
1512
-			],
1513
-		];
1514
-
1515
-		return $data;
1516
-	}
1517
-
1518
-	private function mockSimpleNode(string $class, array $methods): MockObject {
1519
-		$node = $this->createMock($class);
1520
-		foreach ($methods as $method => $return) {
1521
-			if ($method === 'getDirectoryListing') {
1522
-				$return = array_map(
1523
-					fn ($nodeParams) => $this->mockSimpleNode(...$nodeParams),
1524
-					$return
1525
-				);
1526
-			}
1527
-			$node->method($method)->willReturn($return);
1528
-		}
1529
-		return $node;
1530
-	}
1531
-
1532
-	#[DataProvider('dataGetShares')]
1533
-	public function testGetShares(array $getSharesParameters, array $shares, array $extraShareTypes, array $expected): void {
1534
-		$shares = array_map(
1535
-			fn ($sharesByType) => array_map(
1536
-				fn ($shareList) => array_map(
1537
-					function (array $shareParams): IShare {
1538
-						$share = Server::get(IManager::class)->newShare();
1539
-						$share->setShareType($shareParams['type'])
1540
-							->setSharedBy($shareParams['sharedBy'])
1541
-							->setShareOwner($shareParams['owner'])
1542
-							->setPermissions(Constants::PERMISSION_READ)
1543
-							->setId($shareParams['id']);
1544
-						if (isset($shareParams['sharedWith'])) {
1545
-							$share->setSharedWith($shareParams['sharedWith']);
1546
-						}
1547
-						if (isset($shareParams['sharedWithDisplayName'])) {
1548
-							$share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
1549
-						}
1550
-						if (isset($shareParams['sharedWithAvatar'])) {
1551
-							$share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
1552
-						}
1553
-						if (isset($shareParams['attributes'])) {
1554
-							$shareAttributes = $this->createMock(IShareAttributes::class);
1555
-							$shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
1556
-							$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
1557
-							$share->setAttributes($shareAttributes);
1558
-
1559
-							$expects['attributes'] = \json_encode($shareParams['attributes']);
1560
-						}
1561
-						if (isset($shareParams['node'])) {
1562
-							$node = $this->mockSimpleNode(...$shareParams['node']);
1563
-							$share->setNode($node);
1564
-						}
1565
-						if (isset($shareParams['note'])) {
1566
-							$share->setNote($shareParams['note']);
1567
-						}
1568
-						if (isset($shareParams['expirationDate'])) {
1569
-							$share->setExpirationDate($shareParams['expirationDate']);
1570
-						}
1571
-						if (isset($shareParams['token'])) {
1572
-							$share->setToken($shareParams['token']);
1573
-						}
1574
-						if (isset($shareParams['label'])) {
1575
-							$share->setLabel($shareParams['label']);
1576
-						}
1577
-						if (isset($shareParams['password'])) {
1578
-							$share->setPassword($shareParams['password']);
1579
-						}
1580
-						if (isset($shareParams['sendPasswordByTalk'])) {
1581
-							$share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
1582
-						}
1583
-						return $share;
1584
-					},
1585
-					$shareList
1586
-				),
1587
-				$sharesByType
1588
-			),
1589
-			$shares
1590
-		);
1591
-
1592
-		/** @var ShareAPIController&MockObject $ocs */
1593
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
1594
-			->setConstructorArgs([
1595
-				$this->appName,
1596
-				$this->request,
1597
-				$this->shareManager,
1598
-				$this->groupManager,
1599
-				$this->userManager,
1600
-				$this->rootFolder,
1601
-				$this->urlGenerator,
1602
-				$this->l,
1603
-				$this->config,
1604
-				$this->appConfig,
1605
-				$this->appManager,
1606
-				$this->serverContainer,
1607
-				$this->userStatusManager,
1608
-				$this->previewManager,
1609
-				$this->dateTimeZone,
1610
-				$this->logger,
1611
-				$this->factory,
1612
-				$this->mailer,
1613
-				$this->tagManager,
1614
-				$this->getEmailValidatorWithStrictEmailCheck(),
1615
-				$this->trustedServers,
1616
-				$this->currentUser,
1617
-			])
1618
-			->onlyMethods(['formatShare'])
1619
-			->getMock();
1620
-
1621
-		$ocs->method('formatShare')
1622
-			->willReturnCallback(
1623
-				function ($share) {
1624
-					return [
1625
-						'id' => $share->getId(),
1626
-						'share_type' => $share->getShareType()
1627
-					];
1628
-				}
1629
-			);
1630
-
1631
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1632
-		$userFolder->method('get')
1633
-			->with('path')
1634
-			->willReturn($this->mockSimpleNode(...$getSharesParameters['node']));
1635
-
1636
-		$this->rootFolder->method('getUserFolder')
1637
-			->with($this->currentUser)
1638
-			->willReturn($userFolder);
1639
-
1640
-		$this->shareManager
1641
-			->method('getSharesBy')
1642
-			->willReturnCallback(
1643
-				function ($user, $shareType, $node) use ($shares) {
1644
-					if (!isset($shares[$node->getName()]) || !isset($shares[$node->getName()][$shareType])) {
1645
-						return [];
1646
-					}
1647
-					return $shares[$node->getName()][$shareType];
1648
-				}
1649
-			);
1650
-
1651
-		$this->shareManager
1652
-			->method('outgoingServer2ServerSharesAllowed')
1653
-			->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE] ?? false);
1654
-
1655
-		$this->shareManager
1656
-			->method('outgoingServer2ServerGroupSharesAllowed')
1657
-			->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE_GROUP] ?? false);
1658
-
1659
-		$this->groupManager
1660
-			->method('isInGroup')
1661
-			->willReturnCallback(
1662
-				function ($user, $group) {
1663
-					return $group === 'currentUserGroup';
1664
-				}
1665
-			);
1666
-
1667
-		$result = $ocs->getShares(
1668
-			$getSharesParameters['sharedWithMe'] ?? 'false',
1669
-			$getSharesParameters['reshares'] ?? 'false',
1670
-			$getSharesParameters['subfiles'] ?? 'false',
1671
-			'path'
1672
-		);
1673
-
1674
-		$this->assertEquals($expected, $result->getData());
1675
-	}
1676
-
1677
-	public function testCanAccessShareAsOwner(): void {
1678
-		$share = $this->createMock(IShare::class);
1679
-		$share->method('getShareOwner')->willReturn($this->currentUser);
1680
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1681
-	}
1682
-
1683
-	public function testCanAccessShareAsSharer(): void {
1684
-		$share = $this->createMock(IShare::class);
1685
-		$share->method('getSharedBy')->willReturn($this->currentUser);
1686
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1687
-	}
1688
-
1689
-	public function testCanAccessShareAsSharee(): void {
1690
-		$share = $this->createMock(IShare::class);
1691
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
1692
-		$share->method('getSharedWith')->willReturn($this->currentUser);
1693
-		$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1694
-	}
1695
-
1696
-	public function testCannotAccessLinkShare(): void {
1697
-		$share = $this->createMock(IShare::class);
1698
-		$share->method('getShareType')->willReturn(IShare::TYPE_LINK);
1699
-		$share->method('getNodeId')->willReturn(42);
1700
-
1701
-		$userFolder = $this->createMock(Folder::class);
1702
-		$this->rootFolder->method('getUserFolder')
1703
-			->with($this->currentUser)
1704
-			->willReturn($userFolder);
1705
-
1706
-		$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1707
-	}
1708
-
1709
-	#[DataProvider('dataCanAccessShareWithPermissions')]
1710
-	public function testCanAccessShareWithPermissions(int $permissions, bool $expected): void {
1711
-		$share = $this->createMock(IShare::class);
1712
-		$share->method('getShareType')->willReturn(IShare::TYPE_USER);
1713
-		$share->method('getSharedWith')->willReturn($this->createMock(IUser::class));
1714
-		$share->method('getNodeId')->willReturn(42);
1715
-
1716
-		$file = $this->createMock(File::class);
1717
-
1718
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1719
-		$userFolder->method('getFirstNodeById')
1720
-			->with($share->getNodeId())
1721
-			->willReturn($file);
1722
-		$userFolder->method('getById')
1723
-			->with($share->getNodeId())
1724
-			->willReturn([$file]);
1725
-		$this->rootFolder->method('getUserFolder')
1726
-			->with($this->currentUser)
1727
-			->willReturn($userFolder);
1728
-
1729
-		$file->method('getPermissions')
1730
-			->willReturn($permissions);
1731
-
1732
-		if ($expected) {
1733
-			$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1734
-		} else {
1735
-			$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1736
-		}
1737
-	}
1738
-
1739
-	public static function dataCanAccessShareWithPermissions(): array {
1740
-		return [
1741
-			[Constants::PERMISSION_SHARE, true],
1742
-			[Constants::PERMISSION_READ, false],
1743
-			[Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, true],
1744
-		];
1745
-	}
1746
-
1747
-	#[DataProvider('dataCanAccessShareAsGroupMember')]
1748
-	public function testCanAccessShareAsGroupMember(string $group, bool $expected): void {
1749
-		$share = $this->createMock(IShare::class);
1750
-		$share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
1751
-		$share->method('getSharedWith')->willReturn($group);
1752
-		$share->method('getNodeId')->willReturn(42);
1753
-
1754
-		$file = $this->createMock(File::class);
1755
-
1756
-		$userFolder = $this->createMock(Folder::class);
1757
-		$userFolder->method('getFirstNodeById')
1758
-			->with($share->getNodeId())
1759
-			->willReturn($file);
1760
-		$userFolder->method('getById')
1761
-			->with($share->getNodeId())
1762
-			->willReturn([$file]);
1763
-		$this->rootFolder->method('getUserFolder')
1764
-			->with($this->currentUser)
1765
-			->willReturn($userFolder);
1766
-
1767
-		$user = $this->createMock(IUser::class);
1768
-		$this->userManager->method('get')
1769
-			->with($this->currentUser)
1770
-			->willReturn($user);
1771
-
1772
-		$group = $this->createMock(IGroup::class);
1773
-		$group->method('inGroup')->with($user)->willReturn(true);
1774
-		$group2 = $this->createMock(IGroup::class);
1775
-		$group2->method('inGroup')->with($user)->willReturn(false);
1776
-
1777
-		$this->groupManager->method('get')->willReturnMap([
1778
-			['group', $group],
1779
-			['group2', $group2],
1780
-			['group-null', null],
1781
-		]);
1782
-
1783
-		if ($expected) {
1784
-			$this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1785
-		} else {
1786
-			$this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1787
-		}
1788
-	}
1789
-
1790
-	public static function dataCanAccessShareAsGroupMember(): array {
1791
-		return [
1792
-			['group', true],
1793
-			['group2', false],
1794
-			['group-null', false],
1795
-		];
1796
-	}
1797
-
1798
-	public static function dataCanAccessRoomShare(): array {
1799
-		return [
1800
-			[false, false, false],
1801
-			[false, false, true],
1802
-			[true, true, true],
1803
-			[false, true, false],
1804
-		];
1805
-	}
1806
-
1807
-	#[DataProvider('dataCanAccessRoomShare')]
1808
-	public function testCanAccessRoomShare(
1809
-		bool $expected,
1810
-		bool $helperAvailable,
1811
-		bool $canAccessShareByHelper,
1812
-	): void {
1813
-		$share = $this->createMock(IShare::class);
1814
-		$share->method('getShareType')->willReturn(IShare::TYPE_ROOM);
1815
-		$share->method('getSharedWith')->willReturn('recipientRoom');
1816
-
1817
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1818
-		$this->rootFolder->method('getUserFolder')
1819
-			->with($this->currentUser)
1820
-			->willReturn($userFolder);
1821
-
1822
-		$userFolder->method('getById')
1823
-			->with($share->getNodeId())
1824
-			->willReturn([$share->getNode()]);
1825
-
1826
-		if (!$helperAvailable) {
1827
-			$this->appManager->method('isEnabledForUser')
1828
-				->with('spreed')
1829
-				->willReturn(false);
1830
-		} else {
1831
-			$this->appManager->method('isEnabledForUser')
1832
-				->with('spreed')
1833
-				->willReturn(true);
1834
-
1835
-			// This is not possible anymore with PHPUnit 10+
1836
-			// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
1837
-			// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
1838
-			$helper = $this->getMockBuilder(\stdClass::class)
1839
-				->addMethods(['canAccessShare'])
1840
-				->getMock();
1841
-			$helper->method('canAccessShare')
1842
-				->with($share, $this->currentUser)
1843
-				->willReturn($canAccessShareByHelper);
1844
-
1845
-			$this->serverContainer->method('get')
1846
-				->with('\OCA\Talk\Share\Helper\ShareAPIController')
1847
-				->willReturn($helper);
1848
-		}
1849
-
1850
-		$this->assertEquals($expected, $this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1851
-	}
1852
-
1853
-
1854
-	public function testCreateShareNoPath(): void {
1855
-		$this->expectException(OCSNotFoundException::class);
1856
-		$this->expectExceptionMessage('Please specify a file or folder path');
1857
-
1858
-		$this->ocs->createShare();
1859
-	}
1860
-
1861
-
1862
-	public function testCreateShareInvalidPath(): void {
1863
-		$this->expectException(OCSNotFoundException::class);
1864
-		$this->expectExceptionMessage('Wrong path, file/folder does not exist');
1865
-
1866
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
1867
-		$this->rootFolder->expects($this->once())
1868
-			->method('getUserFolder')
1869
-			->with('currentUser')
1870
-			->willReturn($userFolder);
1871
-
1872
-		$userFolder->expects($this->once())
1873
-			->method('get')
1874
-			->with('invalid-path')
1875
-			->willThrowException(new NotFoundException());
1876
-
1877
-		$this->ocs->createShare('invalid-path');
1878
-	}
1879
-
1880
-	public function testCreateShareInvalidShareType(): void {
1881
-		$this->expectException(OCSBadRequestException::class);
1882
-		$this->expectExceptionMessage('Unknown share type');
1883
-
1884
-		$share = $this->newShare();
1885
-		$this->shareManager->method('newShare')->willReturn($share);
1886
-
1887
-		[$userFolder, $file] = $this->getNonSharedUserFile();
1888
-		$this->rootFolder->expects($this->atLeastOnce())
1889
-			->method('getUserFolder')
1890
-			->with('currentUser')
1891
-			->willReturn($userFolder);
1892
-
1893
-		$userFolder->expects($this->atLeastOnce())
1894
-			->method('get')
1895
-			->with('valid-path')
1896
-			->willReturn($file);
1897
-		$userFolder->method('getById')
1898
-			->willReturn([]);
1899
-
1900
-		$file->expects($this->once())
1901
-			->method('lock')
1902
-			->with(ILockingProvider::LOCK_SHARED);
1903
-
1904
-		$this->ocs->createShare('valid-path', 31);
1905
-	}
1906
-
1907
-	public function testCreateShareUserNoShareWith(): void {
1908
-		$this->expectException(OCSNotFoundException::class);
1909
-		$this->expectExceptionMessage('Please specify a valid account to share with');
1910
-
1911
-		$share = $this->newShare();
1912
-		$this->shareManager->method('newShare')->willReturn($share);
1913
-
1914
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1915
-		$this->rootFolder->method('getUserFolder')
1916
-			->with('currentUser')
1917
-			->willReturn($userFolder);
1918
-
1919
-		$userFolder->expects($this->once())
1920
-			->method('get')
1921
-			->with('valid-path')
1922
-			->willReturn($path);
1923
-		$userFolder->method('getById')
1924
-			->willReturn([]);
1925
-
1926
-		$path->expects($this->once())
1927
-			->method('lock')
1928
-			->with(ILockingProvider::LOCK_SHARED);
1929
-
1930
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER);
1931
-	}
1932
-
1933
-
1934
-	public function testCreateShareUserNoValidShareWith(): void {
1935
-		$this->expectException(OCSNotFoundException::class);
1936
-		$this->expectExceptionMessage('Please specify a valid account to share with');
1937
-
1938
-		$share = $this->newShare();
1939
-		$this->shareManager->method('newShare')->willReturn($share);
1940
-
1941
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1942
-		$this->rootFolder->method('getUserFolder')
1943
-			->with('currentUser')
1944
-			->willReturn($userFolder);
1945
-
1946
-		$userFolder->expects($this->once())
1947
-			->method('get')
1948
-			->with('valid-path')
1949
-			->willReturn($path);
1950
-		$userFolder->method('getById')
1951
-			->willReturn([]);
1952
-		$path->expects($this->once())
1953
-			->method('lock')
1954
-			->with(ILockingProvider::LOCK_SHARED);
1955
-		$this->userManager->method('userExists')
1956
-			->with('invalidUser')
1957
-			->willReturn(false);
1958
-
1959
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'invalidUser');
1960
-	}
1961
-
1962
-	public function testCreateShareUser(): void {
1963
-		$share = $this->newShare();
1964
-		$this->shareManager->method('newShare')->willReturn($share);
1965
-
1966
-		/** @var ShareAPIController $ocs */
1967
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
1968
-			->setConstructorArgs([
1969
-				$this->appName,
1970
-				$this->request,
1971
-				$this->shareManager,
1972
-				$this->groupManager,
1973
-				$this->userManager,
1974
-				$this->rootFolder,
1975
-				$this->urlGenerator,
1976
-				$this->l,
1977
-				$this->config,
1978
-				$this->appConfig,
1979
-				$this->appManager,
1980
-				$this->serverContainer,
1981
-				$this->userStatusManager,
1982
-				$this->previewManager,
1983
-				$this->dateTimeZone,
1984
-				$this->logger,
1985
-				$this->factory,
1986
-				$this->mailer,
1987
-				$this->tagManager,
1988
-				$this->getEmailValidatorWithStrictEmailCheck(),
1989
-				$this->trustedServers,
1990
-				$this->currentUser,
1991
-			])->onlyMethods(['formatShare'])
1992
-			->getMock();
1993
-
1994
-		[$userFolder, $path] = $this->getNonSharedUserFile();
1995
-		$this->rootFolder->expects($this->exactly(2))
1996
-			->method('getUserFolder')
1997
-			->with('currentUser')
1998
-			->willReturn($userFolder);
1999
-
2000
-		$userFolder->expects($this->once())
2001
-			->method('get')
2002
-			->with('valid-path')
2003
-			->willReturn($path);
2004
-		$userFolder->method('getById')
2005
-			->willReturn([]);
2006
-
2007
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2008
-
2009
-		$path->expects($this->once())
2010
-			->method('lock')
2011
-			->with(ILockingProvider::LOCK_SHARED);
2012
-
2013
-		$this->shareManager->method('createShare')
2014
-			->with($this->callback(function (IShare $share) use ($path) {
2015
-				return $share->getNode() === $path
2016
-					&& $share->getPermissions() === (
2017
-						Constants::PERMISSION_ALL
2018
-						& ~Constants::PERMISSION_DELETE
2019
-						& ~Constants::PERMISSION_CREATE
2020
-					)
2021
-					&& $share->getShareType() === IShare::TYPE_USER
2022
-					&& $share->getSharedWith() === 'validUser'
2023
-					&& $share->getSharedBy() === 'currentUser';
2024
-			}))
2025
-			->willReturnArgument(0);
2026
-
2027
-		$expected = new DataResponse([]);
2028
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2029
-
2030
-		$this->assertInstanceOf(get_class($expected), $result);
2031
-		$this->assertEquals($expected->getData(), $result->getData());
2032
-	}
2033
-
2034
-
2035
-	public function testCreateShareGroupNoValidShareWith(): void {
2036
-		$this->expectException(OCSNotFoundException::class);
2037
-		$this->expectExceptionMessage('Please specify a valid group');
2038
-
2039
-		$share = $this->newShare();
2040
-		$this->shareManager->method('newShare')->willReturn($share);
2041
-		$this->shareManager->method('createShare')->willReturnArgument(0);
2042
-		$this->shareManager->method('allowGroupSharing')->willReturn(true);
2043
-
2044
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2045
-		$this->rootFolder->method('getUserFolder')
2046
-			->with('currentUser')
2047
-			->willReturn($userFolder);
2048
-
2049
-		$userFolder->expects($this->once())
2050
-			->method('get')
2051
-			->with('valid-path')
2052
-			->willReturn($path);
2053
-		$userFolder->method('getById')
2054
-			->willReturn([]);
2055
-
2056
-		$path->expects($this->once())
2057
-			->method('lock')
2058
-			->with(ILockingProvider::LOCK_SHARED);
2059
-
2060
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2061
-	}
2062
-
2063
-	public function testCreateShareGroup(): void {
2064
-		$share = $this->newShare();
2065
-		$this->shareManager->method('newShare')->willReturn($share);
2066
-
2067
-		/** @var ShareAPIController&MockObject $ocs */
2068
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2069
-			->setConstructorArgs([
2070
-				$this->appName,
2071
-				$this->request,
2072
-				$this->shareManager,
2073
-				$this->groupManager,
2074
-				$this->userManager,
2075
-				$this->rootFolder,
2076
-				$this->urlGenerator,
2077
-				$this->l,
2078
-				$this->config,
2079
-				$this->appConfig,
2080
-				$this->appManager,
2081
-				$this->serverContainer,
2082
-				$this->userStatusManager,
2083
-				$this->previewManager,
2084
-				$this->dateTimeZone,
2085
-				$this->logger,
2086
-				$this->factory,
2087
-				$this->mailer,
2088
-				$this->tagManager,
2089
-				$this->getEmailValidatorWithStrictEmailCheck(),
2090
-				$this->trustedServers,
2091
-				$this->currentUser,
2092
-			])->onlyMethods(['formatShare'])
2093
-			->getMock();
2094
-
2095
-		$this->request
2096
-			->method('getParam')
2097
-			->willReturnMap([
2098
-				['path', null, 'valid-path'],
2099
-				['permissions', null, Constants::PERMISSION_ALL],
2100
-				['shareType', '-1', IShare::TYPE_GROUP],
2101
-				['shareWith', null, 'validGroup'],
2102
-			]);
2103
-
2104
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2105
-		$this->rootFolder->expects($this->exactly(2))
2106
-			->method('getUserFolder')
2107
-			->with('currentUser')
2108
-			->willReturn($userFolder);
2109
-
2110
-		$userFolder->expects($this->once())
2111
-			->method('get')
2112
-			->with('valid-path')
2113
-			->willReturn($path);
2114
-		$userFolder->method('getById')
2115
-			->willReturn([]);
2116
-
2117
-		$this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2118
-
2119
-		$this->shareManager->expects($this->once())
2120
-			->method('allowGroupSharing')
2121
-			->willReturn(true);
2122
-
2123
-		$path->expects($this->once())
2124
-			->method('lock')
2125
-			->with(ILockingProvider::LOCK_SHARED);
2126
-
2127
-		$this->shareManager->method('createShare')
2128
-			->with($this->callback(function (IShare $share) use ($path) {
2129
-				return $share->getNode() === $path
2130
-				&& $share->getPermissions() === Constants::PERMISSION_ALL
2131
-				&& $share->getShareType() === IShare::TYPE_GROUP
2132
-				&& $share->getSharedWith() === 'validGroup'
2133
-				&& $share->getSharedBy() === 'currentUser';
2134
-			}))
2135
-			->willReturnArgument(0);
2136
-
2137
-		$expected = new DataResponse([]);
2138
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'validGroup');
2139
-
2140
-		$this->assertInstanceOf(get_class($expected), $result);
2141
-		$this->assertEquals($expected->getData(), $result->getData());
2142
-	}
2143
-
2144
-
2145
-	public function testCreateShareGroupNotAllowed(): void {
2146
-		$this->expectException(OCSNotFoundException::class);
2147
-		$this->expectExceptionMessage('Group sharing is disabled by the administrator');
2148
-
2149
-		$share = $this->newShare();
2150
-		$this->shareManager->method('newShare')->willReturn($share);
2151
-
2152
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2153
-		$this->rootFolder->method('getUserFolder')
2154
-			->with('currentUser')
2155
-			->willReturn($userFolder);
2156
-
2157
-		$userFolder->expects($this->once())
2158
-			->method('get')
2159
-			->with('valid-path')
2160
-			->willReturn($path);
2161
-		$userFolder->method('getById')
2162
-			->willReturn([]);
2163
-
2164
-		$this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2165
-
2166
-		$this->shareManager->expects($this->once())
2167
-			->method('allowGroupSharing')
2168
-			->willReturn(false);
2169
-
2170
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2171
-	}
2172
-
2173
-
2174
-	public function testCreateShareLinkNoLinksAllowed(): void {
2175
-		$this->expectException(OCSNotFoundException::class);
2176
-		$this->expectExceptionMessage('Public link sharing is disabled by the administrator');
2177
-
2178
-		$this->request
2179
-			->method('getParam')
2180
-			->willReturnMap([
2181
-				['path', null, 'valid-path'],
2182
-				['shareType', '-1', IShare::TYPE_LINK],
2183
-			]);
2184
-
2185
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2186
-		$path->method('getId')->willReturn(42);
2187
-		$storage = $this->createMock(IStorage::class);
2188
-		$storage->method('instanceOfStorage')
2189
-			->willReturnMap([
2190
-				['OCA\Files_Sharing\External\Storage', false],
2191
-				['OCA\Files_Sharing\SharedStorage', false],
2192
-			]);
2193
-		$path->method('getStorage')->willReturn($storage);
2194
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2195
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2196
-		$this->rootFolder->method('getById')
2197
-			->willReturn([]);
2198
-
2199
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2200
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2201
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(false);
2202
-
2203
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK);
2204
-	}
2205
-
2206
-
2207
-	public function testCreateShareLinkNoPublicUpload(): void {
2208
-		$this->expectException(OCSForbiddenException::class);
2209
-		$this->expectExceptionMessage('Public upload disabled by the administrator');
2210
-
2211
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2212
-		$path->method('getId')->willReturn(42);
2213
-		$storage = $this->createMock(IStorage::class);
2214
-		$storage->method('instanceOfStorage')
2215
-			->willReturnMap([
2216
-				['OCA\Files_Sharing\External\Storage', false],
2217
-				['OCA\Files_Sharing\SharedStorage', false],
2218
-			]);
2219
-		$path->method('getStorage')->willReturn($storage);
2220
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2221
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2222
-		$this->rootFolder->method('getById')
2223
-			->willReturn([]);
2224
-
2225
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2226
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2227
-
2228
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2229
-	}
2230
-
2231
-
2232
-	public function testCreateShareLinkPublicUploadFile(): void {
2233
-		$this->expectException(OCSBadRequestException::class);
2234
-		$this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
2235
-
2236
-		$storage = $this->createMock(IStorage::class);
2237
-		$storage->method('instanceOfStorage')
2238
-			->willReturnMap([
2239
-				['OCA\Files_Sharing\External\Storage', false],
2240
-				['OCA\Files_Sharing\SharedStorage', false],
2241
-			]);
2242
-
2243
-		$file = $this->createMock(File::class);
2244
-		$file->method('getId')->willReturn(42);
2245
-		$file->method('getStorage')->willReturn($storage);
2246
-
2247
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2248
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($file);
2249
-		$this->rootFolder->method('getById')
2250
-			->willReturn([]);
2251
-
2252
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2253
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2254
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2255
-
2256
-		$this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2257
-	}
2258
-
2259
-	public function testCreateShareLinkPublicUploadFolder(): void {
2260
-		$ocs = $this->mockFormatShare();
2261
-
2262
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2263
-		$path->method('getId')->willReturn(1);
2264
-		$storage = $this->createMock(IStorage::class);
2265
-		$storage->method('instanceOfStorage')
2266
-			->willReturnMap([
2267
-				['OCA\Files_Sharing\External\Storage', false],
2268
-				['OCA\Files_Sharing\SharedStorage', false],
2269
-			]);
2270
-		$path->method('getStorage')->willReturn($storage);
2271
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2272
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2273
-		$this->rootFolder->method('getById')
2274
-			->willReturn([]);
2275
-
2276
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2277
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2278
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2279
-
2280
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2281
-			$this->callback(function (IShare $share) use ($path) {
2282
-				return $share->getNode() === $path
2283
-					&& $share->getShareType() === IShare::TYPE_LINK
2284
-					&& $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
2285
-					&& $share->getSharedBy() === 'currentUser'
2286
-					&& $share->getPassword() === null
2287
-					&& $share->getExpirationDate() === null;
2288
-			})
2289
-		)->willReturnArgument(0);
2290
-
2291
-		$expected = new DataResponse([]);
2292
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', '', null, '');
2293
-
2294
-		$this->assertInstanceOf(get_class($expected), $result);
2295
-		$this->assertEquals($expected->getData(), $result->getData());
2296
-	}
2297
-
2298
-	public function testCreateShareLinkPassword(): void {
2299
-		$ocs = $this->mockFormatShare();
2300
-
2301
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2302
-		$path->method('getId')->willReturn(42);
2303
-		$storage = $this->createMock(IStorage::class);
2304
-		$storage->method('instanceOfStorage')
2305
-			->willReturnMap([
2306
-				['OCA\Files_Sharing\External\Storage', false],
2307
-				['OCA\Files_Sharing\SharedStorage', false],
2308
-			]);
2309
-		$path->method('getStorage')->willReturn($storage);
2310
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2311
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2312
-		$this->rootFolder->method('getById')
2313
-			->willReturn([]);
2314
-
2315
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2316
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2317
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2318
-
2319
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2320
-			$this->callback(function (IShare $share) use ($path) {
2321
-				return $share->getNode() === $path
2322
-				&& $share->getShareType() === IShare::TYPE_LINK
2323
-				&& $share->getPermissions() === Constants::PERMISSION_READ // publicUpload was set to false
2324
-				&& $share->getSharedBy() === 'currentUser'
2325
-				&& $share->getPassword() === 'password'
2326
-				&& $share->getExpirationDate() === null;
2327
-			})
2328
-		)->willReturnArgument(0);
2329
-
2330
-		$expected = new DataResponse([]);
2331
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_READ, IShare::TYPE_LINK, null, 'false', 'password', null, '');
2332
-
2333
-		$this->assertInstanceOf(get_class($expected), $result);
2334
-		$this->assertEquals($expected->getData(), $result->getData());
2335
-	}
2336
-
2337
-	public function testCreateShareLinkSendPasswordByTalk(): void {
2338
-		$ocs = $this->mockFormatShare();
2339
-
2340
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2341
-		$path->method('getId')->willReturn(42);
2342
-		$storage = $this->createMock(IStorage::class);
2343
-		$storage->method('instanceOfStorage')
2344
-			->willReturnMap([
2345
-				['OCA\Files_Sharing\External\Storage', false],
2346
-				['OCA\Files_Sharing\SharedStorage', false],
2347
-			]);
2348
-		$path->method('getStorage')->willReturn($storage);
2349
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2350
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2351
-		$this->rootFolder->method('getById')
2352
-			->willReturn([]);
2353
-
2354
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2355
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2356
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2357
-
2358
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
2359
-
2360
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2361
-			$this->callback(function (IShare $share) use ($path) {
2362
-				return $share->getNode() === $path
2363
-				&& $share->getShareType() === IShare::TYPE_LINK
2364
-				&& $share->getPermissions() === (Constants::PERMISSION_ALL & ~(Constants::PERMISSION_SHARE))
2365
-				&& $share->getSharedBy() === 'currentUser'
2366
-				&& $share->getPassword() === 'password'
2367
-				&& $share->getSendPasswordByTalk() === true
2368
-				&& $share->getExpirationDate() === null;
2369
-			})
2370
-		)->willReturnArgument(0);
2371
-
2372
-		$expected = new DataResponse([]);
2373
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', 'password', 'true', '');
2374
-
2375
-		$this->assertInstanceOf(get_class($expected), $result);
2376
-		$this->assertEquals($expected->getData(), $result->getData());
2377
-	}
2378
-
2379
-
2380
-	public function testCreateShareLinkSendPasswordByTalkWithTalkDisabled(): void {
2381
-		$this->expectException(OCSForbiddenException::class);
2382
-		$this->expectExceptionMessage('Sharing valid-path sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled');
2383
-
2384
-		$ocs = $this->mockFormatShare();
2385
-
2386
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2387
-		$path->method('getId')->willReturn(42);
2388
-		$storage = $this->createMock(IStorage::class);
2389
-		$storage->method('instanceOfStorage')
2390
-			->willReturnMap([
2391
-				['OCA\Files_Sharing\External\Storage', false],
2392
-				['OCA\Files_Sharing\SharedStorage', false],
2393
-			]);
2394
-		$path->method('getStorage')->willReturn($storage);
2395
-		$path->method('getPath')->willReturn('valid-path');
2396
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2397
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2398
-		$this->rootFolder->method('getById')
2399
-			->willReturn([]);
2400
-
2401
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2402
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2403
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2404
-
2405
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
2406
-
2407
-		$this->shareManager->expects($this->never())->method('createShare');
2408
-
2409
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', 'password', 'true', '');
2410
-	}
2411
-
2412
-	public function testCreateShareValidExpireDate(): void {
2413
-		$ocs = $this->mockFormatShare();
2414
-
2415
-		$this->request
2416
-			->method('getParam')
2417
-			->willReturnMap([
2418
-				['path', null, 'valid-path'],
2419
-				['shareType', '-1', IShare::TYPE_LINK],
2420
-				['publicUpload', null, 'false'],
2421
-				['expireDate', '', '2000-01-01'],
2422
-				['password', '', ''],
2423
-			]);
2424
-
2425
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2426
-		$path->method('getId')->willReturn(42);
2427
-		$storage = $this->createMock(IStorage::class);
2428
-		$storage->method('instanceOfStorage')
2429
-			->willReturnMap([
2430
-				['OCA\Files_Sharing\External\Storage', false],
2431
-				['OCA\Files_Sharing\SharedStorage', false],
2432
-			]);
2433
-		$path->method('getStorage')->willReturn($storage);
2434
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2435
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2436
-		$this->rootFolder->method('getById')
2437
-			->willReturn([]);
2438
-
2439
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2440
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2441
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2442
-
2443
-		$this->shareManager->expects($this->once())->method('createShare')->with(
2444
-			$this->callback(function (IShare $share) use ($path) {
2445
-				$date = new \DateTime('2000-01-01');
2446
-				$date->setTime(0, 0, 0);
2447
-
2448
-				return $share->getNode() === $path
2449
-				&& $share->getShareType() === IShare::TYPE_LINK
2450
-				&& $share->getPermissions() === Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
2451
-				&& $share->getSharedBy() === 'currentUser'
2452
-				&& $share->getPassword() === null
2453
-				&& $share->getExpirationDate() == $date;
2454
-			})
2455
-		)->willReturnArgument(0);
2456
-
2457
-		$expected = new DataResponse([]);
2458
-		$result = $ocs->createShare('valid-path', null, IShare::TYPE_LINK, null, 'false', '', null, '2000-01-01');
2459
-
2460
-		$this->assertInstanceOf(get_class($expected), $result);
2461
-		$this->assertEquals($expected->getData(), $result->getData());
2462
-	}
2463
-
2464
-
2465
-	public function testCreateShareInvalidExpireDate(): void {
2466
-		$this->expectException(OCSNotFoundException::class);
2467
-		$this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
2468
-
2469
-		$ocs = $this->mockFormatShare();
2470
-
2471
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2472
-		$path->method('getId')->willReturn(42);
2473
-		$storage = $this->createMock(IStorage::class);
2474
-		$storage->method('instanceOfStorage')
2475
-			->willReturnMap([
2476
-				['OCA\Files_Sharing\External\Storage', false],
2477
-				['OCA\Files_Sharing\SharedStorage', false],
2478
-			]);
2479
-		$path->method('getStorage')->willReturn($storage);
2480
-		$this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2481
-		$this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2482
-		$this->rootFolder->method('getById')
2483
-			->willReturn([]);
2484
-
2485
-		$this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2486
-		$this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2487
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2488
-
2489
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, 'a1b2d3');
2490
-	}
2491
-
2492
-	public function testCreateShareRemote(): void {
2493
-		$share = $this->newShare();
2494
-		$this->shareManager->method('newShare')->willReturn($share);
2495
-
2496
-		/** @var ShareAPIController $ocs */
2497
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2498
-			->setConstructorArgs([
2499
-				$this->appName,
2500
-				$this->request,
2501
-				$this->shareManager,
2502
-				$this->groupManager,
2503
-				$this->userManager,
2504
-				$this->rootFolder,
2505
-				$this->urlGenerator,
2506
-				$this->l,
2507
-				$this->config,
2508
-				$this->appConfig,
2509
-				$this->appManager,
2510
-				$this->serverContainer,
2511
-				$this->userStatusManager,
2512
-				$this->previewManager,
2513
-				$this->dateTimeZone,
2514
-				$this->logger,
2515
-				$this->factory,
2516
-				$this->mailer,
2517
-				$this->tagManager,
2518
-				$this->getEmailValidatorWithStrictEmailCheck(),
2519
-				$this->trustedServers,
2520
-				$this->currentUser,
2521
-			])->onlyMethods(['formatShare'])
2522
-			->getMock();
2523
-
2524
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2525
-		$this->rootFolder->expects($this->exactly(2))
2526
-			->method('getUserFolder')
2527
-			->with('currentUser')
2528
-			->willReturn($userFolder);
2529
-
2530
-		$userFolder->expects($this->once())
2531
-			->method('get')
2532
-			->with('valid-path')
2533
-			->willReturn($path);
2534
-		$userFolder->method('getById')
2535
-			->willReturn([]);
2536
-
2537
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2538
-
2539
-		$path->expects($this->once())
2540
-			->method('lock')
2541
-			->with(ILockingProvider::LOCK_SHARED);
2542
-
2543
-		$this->shareManager->method('createShare')
2544
-			->with($this->callback(function (IShare $share) use ($path) {
2545
-				return $share->getNode() === $path
2546
-					&& $share->getPermissions() === (
2547
-						Constants::PERMISSION_ALL
2548
-						& ~Constants::PERMISSION_DELETE
2549
-						& ~Constants::PERMISSION_CREATE
2550
-					)
2551
-					&& $share->getShareType() === IShare::TYPE_REMOTE
2552
-					&& $share->getSharedWith() === '[email protected]'
2553
-					&& $share->getSharedBy() === 'currentUser';
2554
-			}))
2555
-			->willReturnArgument(0);
2556
-
2557
-		$this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
2558
-
2559
-		$expected = new DataResponse([]);
2560
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE, '[email protected]');
2561
-
2562
-		$this->assertInstanceOf(get_class($expected), $result);
2563
-		$this->assertEquals($expected->getData(), $result->getData());
2564
-	}
2565
-
2566
-	public function testCreateShareRemoteGroup(): void {
2567
-		$share = $this->newShare();
2568
-		$this->shareManager->method('newShare')->willReturn($share);
2569
-
2570
-		/** @var ShareAPIController $ocs */
2571
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2572
-			->setConstructorArgs([
2573
-				$this->appName,
2574
-				$this->request,
2575
-				$this->shareManager,
2576
-				$this->groupManager,
2577
-				$this->userManager,
2578
-				$this->rootFolder,
2579
-				$this->urlGenerator,
2580
-				$this->l,
2581
-				$this->config,
2582
-				$this->appConfig,
2583
-				$this->appManager,
2584
-				$this->serverContainer,
2585
-				$this->userStatusManager,
2586
-				$this->previewManager,
2587
-				$this->dateTimeZone,
2588
-				$this->logger,
2589
-				$this->factory,
2590
-				$this->mailer,
2591
-				$this->tagManager,
2592
-				$this->getEmailValidatorWithStrictEmailCheck(),
2593
-				$this->trustedServers,
2594
-				$this->currentUser,
2595
-			])->onlyMethods(['formatShare'])
2596
-			->getMock();
2597
-
2598
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2599
-		$this->rootFolder->expects($this->exactly(2))
2600
-			->method('getUserFolder')
2601
-			->with('currentUser')
2602
-			->willReturn($userFolder);
2603
-
2604
-		$userFolder->expects($this->once())
2605
-			->method('get')
2606
-			->with('valid-path')
2607
-			->willReturn($path);
2608
-		$userFolder->method('getById')
2609
-			->willReturn([]);
2610
-
2611
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2612
-
2613
-		$path->expects($this->once())
2614
-			->method('lock')
2615
-			->with(ILockingProvider::LOCK_SHARED);
2616
-
2617
-		$this->shareManager->method('createShare')
2618
-			->with($this->callback(function (IShare $share) use ($path) {
2619
-				return $share->getNode() === $path
2620
-					&& $share->getPermissions() === (
2621
-						Constants::PERMISSION_ALL
2622
-						& ~Constants::PERMISSION_DELETE
2623
-						& ~Constants::PERMISSION_CREATE
2624
-					)
2625
-					&& $share->getShareType() === IShare::TYPE_REMOTE_GROUP
2626
-					&& $share->getSharedWith() === '[email protected]'
2627
-					&& $share->getSharedBy() === 'currentUser';
2628
-			}))
2629
-			->willReturnArgument(0);
2630
-
2631
-		$this->shareManager->method('outgoingServer2ServerGroupSharesAllowed')->willReturn(true);
2632
-
2633
-		$expected = new DataResponse([]);
2634
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE_GROUP, '[email protected]');
2635
-
2636
-		$this->assertInstanceOf(get_class($expected), $result);
2637
-		$this->assertEquals($expected->getData(), $result->getData());
2638
-	}
2639
-
2640
-	public function testCreateShareRoom(): void {
2641
-		$ocs = $this->mockFormatShare();
2642
-
2643
-		$share = $this->newShare();
2644
-		$this->shareManager->method('newShare')->willReturn($share);
2645
-
2646
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2647
-		$this->rootFolder->expects($this->exactly(2))
2648
-			->method('getUserFolder')
2649
-			->with('currentUser')
2650
-			->willReturn($userFolder);
2651
-
2652
-		$userFolder->expects($this->once())
2653
-			->method('get')
2654
-			->with('valid-path')
2655
-			->willReturn($path);
2656
-		$userFolder->method('getById')
2657
-			->willReturn([]);
2658
-
2659
-		$path->expects($this->once())
2660
-			->method('lock')
2661
-			->with(ILockingProvider::LOCK_SHARED);
2662
-
2663
-		$this->appManager->method('isEnabledForUser')
2664
-			->with('spreed')
2665
-			->willReturn(true);
2666
-
2667
-		// This is not possible anymore with PHPUnit 10+
2668
-		// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2669
-		// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2670
-		$helper = $this->getMockBuilder(\stdClass::class)
2671
-			->addMethods(['createShare'])
2672
-			->getMock();
2673
-		$helper->method('createShare')
2674
-			->with(
2675
-				$share,
2676
-				'recipientRoom',
2677
-				Constants::PERMISSION_ALL
2678
-				& ~Constants::PERMISSION_DELETE
2679
-				& ~Constants::PERMISSION_CREATE,
2680
-				''
2681
-			)->willReturnCallback(
2682
-				function ($share): void {
2683
-					$share->setSharedWith('recipientRoom');
2684
-					$share->setPermissions(Constants::PERMISSION_ALL);
2685
-				}
2686
-			);
2687
-
2688
-		$this->serverContainer->method('get')
2689
-			->with('\OCA\Talk\Share\Helper\ShareAPIController')
2690
-			->willReturn($helper);
2691
-
2692
-		$this->shareManager->method('createShare')
2693
-			->with($this->callback(function (IShare $share) use ($path) {
2694
-				return $share->getNode() === $path
2695
-					&& $share->getPermissions() === Constants::PERMISSION_ALL
2696
-					&& $share->getShareType() === IShare::TYPE_ROOM
2697
-					&& $share->getSharedWith() === 'recipientRoom'
2698
-					&& $share->getSharedBy() === 'currentUser';
2699
-			}))
2700
-			->willReturnArgument(0);
2701
-
2702
-		$expected = new DataResponse([]);
2703
-		$result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2704
-
2705
-		$this->assertInstanceOf(get_class($expected), $result);
2706
-		$this->assertEquals($expected->getData(), $result->getData());
2707
-	}
2708
-
2709
-
2710
-	public function testCreateShareRoomHelperNotAvailable(): void {
2711
-		$this->expectException(OCSForbiddenException::class);
2712
-		$this->expectExceptionMessage('Sharing valid-path failed because the back end does not support room shares');
2713
-
2714
-		$ocs = $this->mockFormatShare();
2715
-
2716
-		$share = $this->newShare();
2717
-		$this->shareManager->method('newShare')->willReturn($share);
2718
-
2719
-		[$userFolder, $path] = $this->getNonSharedUserFolder();
2720
-		$this->rootFolder->method('getUserFolder')
2721
-			->with('currentUser')
2722
-			->willReturn($userFolder);
2723
-
2724
-		$path->method('getPath')->willReturn('valid-path');
2725
-		$userFolder->expects($this->once())
2726
-			->method('get')
2727
-			->with('valid-path')
2728
-			->willReturn($path);
2729
-		$userFolder->method('getById')
2730
-			->willReturn([]);
2731
-
2732
-		$path->expects($this->once())
2733
-			->method('lock')
2734
-			->with(ILockingProvider::LOCK_SHARED);
2735
-
2736
-		$this->appManager->method('isEnabledForUser')
2737
-			->with('spreed')
2738
-			->willReturn(false);
2739
-
2740
-		$this->shareManager->expects($this->never())->method('createShare');
2741
-
2742
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2743
-	}
2744
-
2745
-
2746
-	public function testCreateShareRoomHelperThrowException(): void {
2747
-		$this->expectException(OCSNotFoundException::class);
2748
-		$this->expectExceptionMessage('Exception thrown by the helper');
2749
-
2750
-		$ocs = $this->mockFormatShare();
2751
-
2752
-		$share = $this->newShare();
2753
-		$share->setSharedBy('currentUser');
2754
-		$this->shareManager->method('newShare')->willReturn($share);
2755
-
2756
-		[$userFolder, $path] = $this->getNonSharedUserFile();
2757
-		$this->rootFolder->method('getUserFolder')
2758
-			->with('currentUser')
2759
-			->willReturn($userFolder);
2760
-
2761
-		$userFolder->expects($this->once())
2762
-			->method('get')
2763
-			->with('valid-path')
2764
-			->willReturn($path);
2765
-		$userFolder->method('getById')
2766
-			->willReturn([]);
2767
-
2768
-		$path->expects($this->once())
2769
-			->method('lock')
2770
-			->with(ILockingProvider::LOCK_SHARED);
2771
-
2772
-		$this->appManager->method('isEnabledForUser')
2773
-			->with('spreed')
2774
-			->willReturn(true);
2775
-
2776
-		// This is not possible anymore with PHPUnit 10+
2777
-		// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2778
-		// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2779
-		$helper = $this->getMockBuilder(\stdClass::class)
2780
-			->addMethods(['createShare'])
2781
-			->getMock();
2782
-		$helper->method('createShare')
2783
-			->with(
2784
-				$share,
2785
-				'recipientRoom',
2786
-				Constants::PERMISSION_ALL & ~(Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE),
2787
-				''
2788
-			)->willReturnCallback(
2789
-				function ($share): void {
2790
-					throw new OCSNotFoundException('Exception thrown by the helper');
2791
-				}
2792
-			);
2793
-
2794
-		$this->serverContainer->method('get')
2795
-			->with('\OCA\Talk\Share\Helper\ShareAPIController')
2796
-			->willReturn($helper);
2797
-
2798
-		$this->shareManager->expects($this->never())->method('createShare');
2799
-
2800
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2801
-	}
2802
-
2803
-	/**
2804
-	 * Test for https://github.com/owncloud/core/issues/22587
2805
-	 * TODO: Remove once proper solution is in place
2806
-	 */
2807
-	public function testCreateReshareOfFederatedMountNoDeletePermissions(): void {
2808
-		$share = Server::get(IManager::class)->newShare();
2809
-		$this->shareManager->method('newShare')->willReturn($share);
2810
-
2811
-		/** @var ShareAPIController&MockObject $ocs */
2812
-		$ocs = $this->getMockBuilder(ShareAPIController::class)
2813
-			->setConstructorArgs([
2814
-				$this->appName,
2815
-				$this->request,
2816
-				$this->shareManager,
2817
-				$this->groupManager,
2818
-				$this->userManager,
2819
-				$this->rootFolder,
2820
-				$this->urlGenerator,
2821
-				$this->l,
2822
-				$this->config,
2823
-				$this->appConfig,
2824
-				$this->appManager,
2825
-				$this->serverContainer,
2826
-				$this->userStatusManager,
2827
-				$this->previewManager,
2828
-				$this->dateTimeZone,
2829
-				$this->logger,
2830
-				$this->factory,
2831
-				$this->mailer,
2832
-				$this->tagManager,
2833
-				$this->getEmailValidatorWithStrictEmailCheck(),
2834
-				$this->trustedServers,
2835
-				$this->currentUser,
2836
-			])->onlyMethods(['formatShare'])
2837
-			->getMock();
2838
-
2839
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
2840
-		$this->rootFolder->expects($this->exactly(2))
2841
-			->method('getUserFolder')
2842
-			->with('currentUser')
2843
-			->willReturn($userFolder);
2844
-
2845
-		$path = $this->getMockBuilder(Folder::class)->getMock();
2846
-		$path->method('getId')->willReturn(42);
2847
-
2848
-		$storage = $this->createMock(IStorage::class);
2849
-		$storage->method('instanceOfStorage')
2850
-			->willReturnMap([
2851
-				['OCA\Files_Sharing\External\Storage', true],
2852
-				['OCA\Files_Sharing\SharedStorage', false],
2853
-			]);
2854
-		$userFolder->method('getStorage')->willReturn($storage);
2855
-		$path->method('getStorage')->willReturn($storage);
2856
-
2857
-		$path->method('getPermissions')->willReturn(Constants::PERMISSION_READ);
2858
-		$userFolder->expects($this->once())
2859
-			->method('get')
2860
-			->with('valid-path')
2861
-			->willReturn($path);
2862
-		$userFolder->method('getById')
2863
-			->willReturn([]);
2864
-
2865
-		$this->userManager->method('userExists')->with('validUser')->willReturn(true);
2866
-
2867
-		$this->shareManager
2868
-			->expects($this->once())
2869
-			->method('createShare')
2870
-			->with($this->callback(function (IShare $share) {
2871
-				return $share->getPermissions() === Constants::PERMISSION_READ;
2872
-			}))
2873
-			->willReturnArgument(0);
2874
-
2875
-		$ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2876
-	}
2877
-
2878
-
2879
-	public function testUpdateShareCantAccess(): void {
2880
-		$this->expectException(OCSNotFoundException::class);
2881
-		$this->expectExceptionMessage('Wrong share ID, share does not exist');
2882
-
2883
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
2884
-		$share = $this->newShare();
2885
-		$share->setNode($node);
2886
-
2887
-		$node->expects($this->once())
2888
-			->method('lock')
2889
-			->with(ILockingProvider::LOCK_SHARED);
2890
-
2891
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2892
-
2893
-		$this->rootFolder->method('getUserFolder')
2894
-			->with($this->currentUser)
2895
-			->willReturn($userFolder);
2896
-
2897
-		$userFolder->method('getById')
2898
-			->with($share->getNodeId())
2899
-			->willReturn([$share->getNode()]);
2900
-
2901
-		$this->ocs->updateShare(42);
2902
-	}
2903
-
2904
-
2905
-	public function testUpdateNoParametersLink(): void {
2906
-		$this->expectException(OCSBadRequestException::class);
2907
-		$this->expectExceptionMessage('Wrong or no update parameter given');
2908
-
2909
-		$node = $this->getMockBuilder(Folder::class)->getMock();
2910
-		$share = $this->newShare();
2911
-		$share->setPermissions(Constants::PERMISSION_ALL)
2912
-			->setSharedBy($this->currentUser)
2913
-			->setShareType(IShare::TYPE_LINK)
2914
-			->setNode($node);
2915
-
2916
-		$node->expects($this->once())
2917
-			->method('lock')
2918
-			->with(ILockingProvider::LOCK_SHARED);
2919
-
2920
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2921
-
2922
-		$this->ocs->updateShare(42);
2923
-	}
2924
-
2925
-
2926
-	public function testUpdateNoParametersOther(): void {
2927
-		$this->expectException(OCSBadRequestException::class);
2928
-		$this->expectExceptionMessage('Wrong or no update parameter given');
2929
-
2930
-		$node = $this->getMockBuilder(Folder::class)->getMock();
2931
-		$share = $this->newShare();
2932
-		$share->setPermissions(Constants::PERMISSION_ALL)
2933
-			->setSharedBy($this->currentUser)
2934
-			->setShareType(IShare::TYPE_GROUP)
2935
-			->setNode($node);
2936
-
2937
-		$node->expects($this->once())
2938
-			->method('lock')
2939
-			->with(ILockingProvider::LOCK_SHARED);
2940
-
2941
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2942
-
2943
-		$this->ocs->updateShare(42);
2944
-	}
2945
-
2946
-	public function testUpdateLinkShareClear(): void {
2947
-		$ocs = $this->mockFormatShare();
2948
-
2949
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
2950
-		$node->method('getId')
2951
-			->willReturn(42);
2952
-		$share = $this->newShare();
2953
-		$share->setPermissions(Constants::PERMISSION_ALL)
2954
-			->setSharedBy($this->currentUser)
2955
-			->setShareType(IShare::TYPE_LINK)
2956
-			->setPassword('password')
2957
-			->setExpirationDate(new \DateTime())
2958
-			->setNote('note')
2959
-			->setLabel('label')
2960
-			->setHideDownload(true)
2961
-			->setPermissions(Constants::PERMISSION_ALL)
2962
-			->setNode($node);
2963
-
2964
-		$node->expects($this->once())
2965
-			->method('lock')
2966
-			->with(ILockingProvider::LOCK_SHARED);
2967
-
2968
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2969
-
2970
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
2971
-			$this->callback(function (IShare $share) {
2972
-				return $share->getPermissions() === Constants::PERMISSION_READ
2973
-				&& $share->getPassword() === null
2974
-				&& $share->getExpirationDate() === null
2975
-				// Once set a note or a label are never back to null, only to an
2976
-				// empty string.
2977
-				&& $share->getNote() === ''
2978
-				&& $share->getLabel() === ''
2979
-				&& $share->getHideDownload() === false;
2980
-			})
2981
-		)->willReturnArgument(0);
2982
-
2983
-		$this->shareManager->method('getSharedWith')
2984
-			->willReturn([]);
2985
-
2986
-		$this->rootFolder->method('getUserFolder')
2987
-			->with($this->currentUser)
2988
-			->willReturn($userFolder);
2989
-
2990
-		$userFolder->method('getById')
2991
-			->with(42)
2992
-			->willReturn([$node]);
2993
-		$userFolder->method('getFirstNodeById')
2994
-			->with(42)
2995
-			->willReturn($node);
2996
-
2997
-		$mountPoint = $this->createMock(IMountPoint::class);
2998
-		$node->method('getMountPoint')
2999
-			->willReturn($mountPoint);
3000
-		$mountPoint->method('getStorageRootId')
3001
-			->willReturn(42);
3002
-
3003
-		$expected = new DataResponse([]);
3004
-		$result = $ocs->updateShare(42, null, '', null, 'false', '', '', '', 'false');
3005
-
3006
-		$this->assertInstanceOf(get_class($expected), $result);
3007
-		$this->assertEquals($expected->getData(), $result->getData());
3008
-	}
3009
-
3010
-	public function testUpdateLinkShareSet(): void {
3011
-		$ocs = $this->mockFormatShare();
3012
-
3013
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3014
-		$folder->method('getId')
3015
-			->willReturn(42);
3016
-
3017
-		$share = Server::get(IManager::class)->newShare();
3018
-		$share->setPermissions(Constants::PERMISSION_ALL)
3019
-			->setSharedBy($this->currentUser)
3020
-			->setShareType(IShare::TYPE_LINK)
3021
-			->setNode($folder);
3022
-
3023
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3024
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3025
-
3026
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3027
-			$this->callback(function (IShare $share) {
3028
-				$date = new \DateTime('2000-01-01');
3029
-				$date->setTime(0, 0, 0);
3030
-
3031
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3032
-				&& $share->getPassword() === 'password'
3033
-				&& $share->getExpirationDate() == $date
3034
-				&& $share->getNote() === 'note'
3035
-				&& $share->getLabel() === 'label'
3036
-				&& $share->getHideDownload() === true;
3037
-			})
3038
-		)->willReturnArgument(0);
3039
-
3040
-		$this->shareManager->method('getSharedWith')
3041
-			->willReturn([]);
3042
-
3043
-		$this->rootFolder->method('getUserFolder')
3044
-			->with($this->currentUser)
3045
-			->willReturn($userFolder);
3046
-
3047
-		$userFolder->method('getById')
3048
-			->with(42)
3049
-			->willReturn([$folder]);
3050
-
3051
-		$mountPoint = $this->createMock(IMountPoint::class);
3052
-		$folder->method('getMountPoint')
3053
-			->willReturn($mountPoint);
3054
-		$mountPoint->method('getStorageRootId')
3055
-			->willReturn(42);
3056
-
3057
-		$expected = new DataResponse([]);
3058
-		$result = $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-01', 'note', 'label', 'true');
3059
-
3060
-		$this->assertInstanceOf(get_class($expected), $result);
3061
-		$this->assertEquals($expected->getData(), $result->getData());
3062
-	}
3063
-
3064
-	#[DataProvider('publicUploadParamsProvider')]
3065
-	public function testUpdateLinkShareEnablePublicUpload($permissions, $publicUpload, $expireDate, $password): void {
3066
-		$ocs = $this->mockFormatShare();
3067
-
3068
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3069
-		$folder->method('getId')
3070
-			->willReturn(42);
3071
-
3072
-		$share = Server::get(IManager::class)->newShare();
3073
-		$share->setPermissions(Constants::PERMISSION_ALL)
3074
-			->setSharedBy($this->currentUser)
3075
-			->setShareType(IShare::TYPE_LINK)
3076
-			->setPassword('password')
3077
-			->setNode($folder);
3078
-
3079
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3080
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3081
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3082
-
3083
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3084
-			$this->callback(function (IShare $share) {
3085
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3086
-				&& $share->getPassword() === 'password'
3087
-				&& $share->getExpirationDate() === null;
3088
-			})
3089
-		)->willReturnArgument(0);
3090
-
3091
-		$this->rootFolder->method('getUserFolder')
3092
-			->with($this->currentUser)
3093
-			->willReturn($userFolder);
3094
-
3095
-		$userFolder->method('getById')
3096
-			->with(42)
3097
-			->willReturn([$folder]);
3098
-
3099
-		$mountPoint = $this->createMock(IMountPoint::class);
3100
-		$folder->method('getMountPoint')
3101
-			->willReturn($mountPoint);
3102
-		$mountPoint->method('getStorageRootId')
3103
-			->willReturn(42);
3104
-
3105
-		$expected = new DataResponse([]);
3106
-		$result = $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3107
-
3108
-		$this->assertInstanceOf(get_class($expected), $result);
3109
-		$this->assertEquals($expected->getData(), $result->getData());
3110
-	}
3111
-
3112
-
3113
-	public static function publicLinkValidPermissionsProvider() {
3114
-		return [
3115
-			[Constants::PERMISSION_CREATE],
3116
-			[Constants::PERMISSION_READ],
3117
-			[Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE],
3118
-			[Constants::PERMISSION_READ | Constants::PERMISSION_DELETE],
3119
-			[Constants::PERMISSION_READ | Constants::PERMISSION_CREATE],
3120
-		];
3121
-	}
3122
-
3123
-	#[DataProvider('publicLinkValidPermissionsProvider')]
3124
-	public function testUpdateLinkShareSetCRUDPermissions($permissions): void {
3125
-		$ocs = $this->mockFormatShare();
3126
-
3127
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3128
-		$folder->method('getId')
3129
-			->willReturn(42);
3130
-
3131
-		$share = Server::get(IManager::class)->newShare();
3132
-		$share->setPermissions(Constants::PERMISSION_ALL)
3133
-			->setSharedBy($this->currentUser)
3134
-			->setShareType(IShare::TYPE_LINK)
3135
-			->setPassword('password')
3136
-			->setNode($folder);
3137
-
3138
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3139
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3140
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3141
-
3142
-		$this->shareManager
3143
-			->expects($this->any())
3144
-			->method('updateShare')
3145
-			->willReturnArgument(0);
3146
-
3147
-		$this->rootFolder->method('getUserFolder')
3148
-			->with($this->currentUser)
3149
-			->willReturn($userFolder);
3150
-
3151
-		$userFolder->method('getById')
3152
-			->with(42)
3153
-			->willReturn([$folder]);
3154
-
3155
-		$mountPoint = $this->createMock(IMountPoint::class);
3156
-		$folder->method('getMountPoint')
3157
-			->willReturn($mountPoint);
3158
-		$mountPoint->method('getStorageRootId')
3159
-			->willReturn(42);
3160
-
3161
-		$expected = new DataResponse([]);
3162
-		$result = $ocs->updateShare(42, $permissions, 'password', null, null, null);
3163
-
3164
-		$this->assertInstanceOf(get_class($expected), $result);
3165
-		$this->assertEquals($expected->getData(), $result->getData());
3166
-	}
3167
-
3168
-	public static function publicLinkInvalidPermissionsProvider1() {
3169
-		return [
3170
-			[Constants::PERMISSION_DELETE],
3171
-			[Constants::PERMISSION_UPDATE],
3172
-			[Constants::PERMISSION_SHARE],
3173
-		];
3174
-	}
3175
-
3176
-	#[DataProvider('publicLinkInvalidPermissionsProvider1')]
3177
-	public function testUpdateLinkShareSetInvalidCRUDPermissions1($permissions): void {
3178
-		$this->expectException(OCSBadRequestException::class);
3179
-		$this->expectExceptionMessage('Share must at least have READ or CREATE permissions');
3180
-
3181
-		$this->testUpdateLinkShareSetCRUDPermissions($permissions, null);
3182
-	}
3183
-
3184
-	public static function publicLinkInvalidPermissionsProvider2() {
3185
-		return [
3186
-			[Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE],
3187
-			[Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE],
3188
-		];
3189
-	}
3190
-
3191
-	#[DataProvider('publicLinkInvalidPermissionsProvider2')]
3192
-	public function testUpdateLinkShareSetInvalidCRUDPermissions2($permissions): void {
3193
-		$this->expectException(OCSBadRequestException::class);
3194
-		$this->expectExceptionMessage('Share must have READ permission if UPDATE or DELETE permission is set');
3195
-
3196
-		$this->testUpdateLinkShareSetCRUDPermissions($permissions);
3197
-	}
3198
-
3199
-	public function testUpdateLinkShareInvalidDate(): void {
3200
-		$this->expectException(OCSBadRequestException::class);
3201
-		$this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
3202
-
3203
-		$ocs = $this->mockFormatShare();
3204
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3205
-		$userFolder->method('getById')
3206
-			->with(42)
3207
-			->willReturn([$folder]);
3208
-		$this->rootFolder->method('getUserFolder')
3209
-			->with($this->currentUser)
3210
-			->willReturn($userFolder);
3211
-
3212
-		$folder->method('getId')
3213
-			->willReturn(42);
3214
-
3215
-		$share = Server::get(IManager::class)->newShare();
3216
-		$share->setPermissions(Constants::PERMISSION_ALL)
3217
-			->setSharedBy($this->currentUser)
3218
-			->setShareType(IShare::TYPE_LINK)
3219
-			->setNode($folder);
3220
-
3221
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3222
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3223
-
3224
-		$ocs->updateShare(42, null, 'password', null, 'true', '2000-01-a');
3225
-	}
3226
-
3227
-	public static function publicUploadParamsProvider() {
3228
-		return [
3229
-			[null, 'true', null, 'password'],
3230
-			// legacy had no delete
3231
-			[
3232
-				Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE,
3233
-				'true', null, 'password'
3234
-			],
3235
-			// correct
3236
-			[
3237
-				Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE,
3238
-				null, null, 'password'
3239
-			],
3240
-		];
3241
-	}
3242
-
3243
-	#[DataProvider('publicUploadParamsProvider')]
3244
-	public function testUpdateLinkSharePublicUploadNotAllowed($permissions, $publicUpload, $expireDate, $password): void {
3245
-		$this->expectException(OCSForbiddenException::class);
3246
-		$this->expectExceptionMessage('Public upload disabled by the administrator');
3247
-
3248
-		$ocs = $this->mockFormatShare();
3249
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3250
-		$userFolder->method('getById')
3251
-			->with(42)
3252
-			->willReturn([$folder]);
3253
-		$this->rootFolder->method('getUserFolder')
3254
-			->with($this->currentUser)
3255
-			->willReturn($userFolder);
3256
-
3257
-		$folder->method('getId')->willReturn(42);
3258
-
3259
-		$share = Server::get(IManager::class)->newShare();
3260
-		$share->setPermissions(Constants::PERMISSION_ALL)
3261
-			->setSharedBy($this->currentUser)
3262
-			->setShareType(IShare::TYPE_LINK)
3263
-			->setNode($folder);
3264
-
3265
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3266
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(false);
3267
-
3268
-		$ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3269
-	}
3270
-
3271
-
3272
-	public function testUpdateLinkSharePublicUploadOnFile(): void {
3273
-		$this->expectException(OCSBadRequestException::class);
3274
-		$this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
3275
-
3276
-		$ocs = $this->mockFormatShare();
3277
-
3278
-		$file = $this->getMockBuilder(File::class)->getMock();
3279
-		$file->method('getId')
3280
-			->willReturn(42);
3281
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3282
-		$userFolder->method('getById')
3283
-			->with(42)
3284
-			->willReturn([$folder]);
3285
-		$this->rootFolder->method('getUserFolder')
3286
-			->with($this->currentUser)
3287
-			->willReturn($userFolder);
3288
-
3289
-		$share = Server::get(IManager::class)->newShare();
3290
-		$share->setPermissions(Constants::PERMISSION_ALL)
3291
-			->setSharedBy($this->currentUser)
3292
-			->setShareType(IShare::TYPE_LINK)
3293
-			->setNode($file);
3294
-
3295
-		$this->shareManager
3296
-			->method('getShareById')
3297
-			->with('ocinternal:42')
3298
-			->willReturn($share);
3299
-		$this->shareManager
3300
-			->method('shareApiLinkAllowPublicUpload')
3301
-			->willReturn(true);
3302
-		$this->shareManager
3303
-			->method('updateShare')
3304
-			->with($share)
3305
-			->willThrowException(new \InvalidArgumentException('File shares cannot have create or delete permissions'));
3306
-
3307
-		$ocs->updateShare(42, null, 'password', null, 'true', '');
3308
-	}
3309
-
3310
-	public function testUpdateLinkSharePasswordDoesNotChangeOther(): void {
3311
-		$ocs = $this->mockFormatShare();
3312
-
3313
-		$date = new \DateTime('2000-01-01');
3314
-		$date->setTime(0, 0, 0);
3315
-
3316
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3317
-		$node->method('getId')->willReturn(42);
3318
-		$userFolder->method('getById')
3319
-			->with(42)
3320
-			->willReturn([$node]);
3321
-		$this->rootFolder->method('getUserFolder')
3322
-			->with($this->currentUser)
3323
-			->willReturn($userFolder);
3324
-		$share = $this->newShare();
3325
-		$share->setPermissions(Constants::PERMISSION_ALL)
3326
-			->setSharedBy($this->currentUser)
3327
-			->setShareType(IShare::TYPE_LINK)
3328
-			->setPassword('password')
3329
-			->setSendPasswordByTalk(true)
3330
-			->setExpirationDate($date)
3331
-			->setNote('note')
3332
-			->setLabel('label')
3333
-			->setHideDownload(true)
3334
-			->setPermissions(Constants::PERMISSION_ALL)
3335
-			->setNode($node);
3336
-
3337
-		$node->expects($this->once())
3338
-			->method('lock')
3339
-			->with(ILockingProvider::LOCK_SHARED);
3340
-
3341
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3342
-
3343
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3344
-			$this->callback(function (IShare $share) use ($date) {
3345
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3346
-				&& $share->getPassword() === 'newpassword'
3347
-				&& $share->getSendPasswordByTalk() === true
3348
-				&& $share->getExpirationDate() === $date
3349
-				&& $share->getNote() === 'note'
3350
-				&& $share->getLabel() === 'label'
3351
-				&& $share->getHideDownload() === true;
3352
-			})
3353
-		)->willReturnArgument(0);
3354
-
3355
-		$expected = new DataResponse([]);
3356
-		$result = $ocs->updateShare(42, null, 'newpassword', null, null, null, null, null, null);
3357
-
3358
-		$this->assertInstanceOf(get_class($expected), $result);
3359
-		$this->assertEquals($expected->getData(), $result->getData());
3360
-	}
3361
-
3362
-	public function testUpdateLinkShareSendPasswordByTalkDoesNotChangeOther(): void {
3363
-		$ocs = $this->mockFormatShare();
3364
-
3365
-		$date = new \DateTime('2000-01-01');
3366
-		$date->setTime(0, 0, 0);
3367
-
3368
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3369
-		$userFolder->method('getById')
3370
-			->with(42)
3371
-			->willReturn([$node]);
3372
-		$this->rootFolder->method('getUserFolder')
3373
-			->with($this->currentUser)
3374
-			->willReturn($userFolder);
3375
-		$node->method('getId')->willReturn(42);
3376
-		$share = $this->newShare();
3377
-		$share->setPermissions(Constants::PERMISSION_ALL)
3378
-			->setSharedBy($this->currentUser)
3379
-			->setShareType(IShare::TYPE_LINK)
3380
-			->setPassword('password')
3381
-			->setSendPasswordByTalk(false)
3382
-			->setExpirationDate($date)
3383
-			->setNote('note')
3384
-			->setLabel('label')
3385
-			->setHideDownload(true)
3386
-			->setPermissions(Constants::PERMISSION_ALL)
3387
-			->setNode($node);
3388
-
3389
-		$node->expects($this->once())
3390
-			->method('lock')
3391
-			->with(ILockingProvider::LOCK_SHARED);
3392
-
3393
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3394
-
3395
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3396
-
3397
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3398
-			$this->callback(function (IShare $share) use ($date) {
3399
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3400
-				&& $share->getPassword() === 'password'
3401
-				&& $share->getSendPasswordByTalk() === true
3402
-				&& $share->getExpirationDate() === $date
3403
-				&& $share->getNote() === 'note'
3404
-				&& $share->getLabel() === 'label'
3405
-				&& $share->getHideDownload() === true;
3406
-			})
3407
-		)->willReturnArgument(0);
3408
-
3409
-		$expected = new DataResponse([]);
3410
-		$result = $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3411
-
3412
-		$this->assertInstanceOf(get_class($expected), $result);
3413
-		$this->assertEquals($expected->getData(), $result->getData());
3414
-	}
3415
-
3416
-
3417
-	public function testUpdateLinkShareSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3418
-		$this->expectException(OCSForbiddenException::class);
3419
-		$this->expectExceptionMessage('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.');
3420
-
3421
-		$ocs = $this->mockFormatShare();
3422
-
3423
-		$date = new \DateTime('2000-01-01');
3424
-		$date->setTime(0, 0, 0);
3425
-
3426
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3427
-		$userFolder->method('getById')
3428
-			->with(42)
3429
-			->willReturn([$node]);
3430
-		$this->rootFolder->method('getUserFolder')
3431
-			->with($this->currentUser)
3432
-			->willReturn($userFolder);
3433
-		$node->method('getId')->willReturn(42);
3434
-		$share = $this->newShare();
3435
-		$share->setPermissions(Constants::PERMISSION_ALL)
3436
-			->setSharedBy($this->currentUser)
3437
-			->setShareType(IShare::TYPE_LINK)
3438
-			->setPassword('password')
3439
-			->setSendPasswordByTalk(false)
3440
-			->setExpirationDate($date)
3441
-			->setNote('note')
3442
-			->setLabel('label')
3443
-			->setHideDownload(true)
3444
-			->setPermissions(Constants::PERMISSION_ALL)
3445
-			->setNode($node);
3446
-
3447
-		$node->expects($this->once())
3448
-			->method('lock')
3449
-			->with(ILockingProvider::LOCK_SHARED);
3450
-
3451
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3452
-
3453
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3454
-
3455
-		$this->shareManager->expects($this->never())->method('updateShare');
3456
-
3457
-		$ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3458
-	}
3459
-
3460
-	public function testUpdateLinkShareDoNotSendPasswordByTalkDoesNotChangeOther(): void {
3461
-		$ocs = $this->mockFormatShare();
3462
-
3463
-		$date = new \DateTime('2000-01-01');
3464
-		$date->setTime(0, 0, 0);
3465
-
3466
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3467
-		$userFolder->method('getById')
3468
-			->with(42)
3469
-			->willReturn([$node]);
3470
-		$this->rootFolder->method('getUserFolder')
3471
-			->with($this->currentUser)
3472
-			->willReturn($userFolder);
3473
-		$node->method('getId')->willReturn(42);
3474
-		$share = $this->newShare();
3475
-		$share->setPermissions(Constants::PERMISSION_ALL)
3476
-			->setSharedBy($this->currentUser)
3477
-			->setShareType(IShare::TYPE_LINK)
3478
-			->setPassword('password')
3479
-			->setSendPasswordByTalk(true)
3480
-			->setExpirationDate($date)
3481
-			->setNote('note')
3482
-			->setLabel('label')
3483
-			->setHideDownload(true)
3484
-			->setPermissions(Constants::PERMISSION_ALL)
3485
-			->setNode($node);
3486
-
3487
-		$node->expects($this->once())
3488
-			->method('lock')
3489
-			->with(ILockingProvider::LOCK_SHARED);
3490
-
3491
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3492
-
3493
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3494
-
3495
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3496
-			$this->callback(function (IShare $share) use ($date) {
3497
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3498
-				&& $share->getPassword() === 'password'
3499
-				&& $share->getSendPasswordByTalk() === false
3500
-				&& $share->getExpirationDate() === $date
3501
-				&& $share->getNote() === 'note'
3502
-				&& $share->getLabel() === 'label'
3503
-				&& $share->getHideDownload() === true;
3504
-			})
3505
-		)->willReturnArgument(0);
3506
-
3507
-		$expected = new DataResponse([]);
3508
-		$result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3509
-
3510
-		$this->assertInstanceOf(get_class($expected), $result);
3511
-		$this->assertEquals($expected->getData(), $result->getData());
3512
-	}
3513
-
3514
-	public function testUpdateLinkShareDoNotSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3515
-		$ocs = $this->mockFormatShare();
3516
-
3517
-		$date = new \DateTime('2000-01-01');
3518
-		$date->setTime(0, 0, 0);
3519
-
3520
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3521
-		$node->method('getId')
3522
-			->willReturn(42);
3523
-
3524
-		$share = $this->newShare();
3525
-		$share->setPermissions(Constants::PERMISSION_ALL)
3526
-			->setSharedBy($this->currentUser)
3527
-			->setShareType(IShare::TYPE_LINK)
3528
-			->setPassword('password')
3529
-			->setSendPasswordByTalk(true)
3530
-			->setExpirationDate($date)
3531
-			->setNote('note')
3532
-			->setLabel('label')
3533
-			->setHideDownload(true)
3534
-			->setPermissions(Constants::PERMISSION_ALL)
3535
-			->setNode($node);
3536
-
3537
-		$node->expects($this->once())
3538
-			->method('lock')
3539
-			->with(ILockingProvider::LOCK_SHARED);
3540
-
3541
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3542
-
3543
-		$this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3544
-
3545
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3546
-			$this->callback(function (IShare $share) use ($date) {
3547
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3548
-				&& $share->getPassword() === 'password'
3549
-				&& $share->getSendPasswordByTalk() === false
3550
-				&& $share->getExpirationDate() === $date
3551
-				&& $share->getNote() === 'note'
3552
-				&& $share->getLabel() === 'label'
3553
-				&& $share->getHideDownload() === true;
3554
-			})
3555
-		)->willReturnArgument(0);
3556
-
3557
-		$this->rootFolder->method('getUserFolder')
3558
-			->with($this->currentUser)
3559
-			->willReturn($userFolder);
3560
-
3561
-		$userFolder->method('getById')
3562
-			->with(42)
3563
-			->willReturn([$node]);
3564
-
3565
-		$mountPoint = $this->createMock(IMountPoint::class);
3566
-		$node->method('getMountPoint')
3567
-			->willReturn($mountPoint);
3568
-		$mountPoint->method('getStorageRootId')
3569
-			->willReturn(42);
3570
-
3571
-		$mountPoint = $this->createMock(IMountPoint::class);
3572
-		$node->method('getMountPoint')
3573
-			->willReturn($mountPoint);
3574
-		$mountPoint->method('getStorageRootId')
3575
-			->willReturn(42);
3576
-
3577
-		$expected = new DataResponse([]);
3578
-		$result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3579
-
3580
-		$this->assertInstanceOf(get_class($expected), $result);
3581
-		$this->assertEquals($expected->getData(), $result->getData());
3582
-	}
3583
-
3584
-	public function testUpdateLinkShareExpireDateDoesNotChangeOther(): void {
3585
-		$ocs = $this->mockFormatShare();
3586
-
3587
-		[$userFolder, $node] = $this->getNonSharedUserFolder();
3588
-		$node->method('getId')
3589
-			->willReturn(42);
3590
-
3591
-		$share = $this->newShare();
3592
-		$share->setPermissions(Constants::PERMISSION_ALL)
3593
-			->setSharedBy($this->currentUser)
3594
-			->setShareType(IShare::TYPE_LINK)
3595
-			->setPassword('password')
3596
-			->setSendPasswordByTalk(true)
3597
-			->setExpirationDate(new \DateTime())
3598
-			->setNote('note')
3599
-			->setLabel('label')
3600
-			->setHideDownload(true)
3601
-			->setPermissions(Constants::PERMISSION_ALL)
3602
-			->setNode($node);
3603
-
3604
-		$node->expects($this->once())
3605
-			->method('lock')
3606
-			->with(ILockingProvider::LOCK_SHARED);
3607
-
3608
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3609
-
3610
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3611
-			$this->callback(function (IShare $share) {
3612
-				$date = new \DateTime('2010-12-23');
3613
-				$date->setTime(0, 0, 0);
3614
-
3615
-				return $share->getPermissions() === Constants::PERMISSION_ALL
3616
-				&& $share->getPassword() === 'password'
3617
-				&& $share->getSendPasswordByTalk() === true
3618
-				&& $share->getExpirationDate() == $date
3619
-				&& $share->getNote() === 'note'
3620
-				&& $share->getLabel() === 'label'
3621
-				&& $share->getHideDownload() === true;
3622
-			})
3623
-		)->willReturnArgument(0);
3624
-
3625
-		$this->rootFolder->method('getUserFolder')
3626
-			->with($this->currentUser)
3627
-			->willReturn($userFolder);
3628
-
3629
-		$userFolder->method('getById')
3630
-			->with(42)
3631
-			->willReturn([$node]);
3632
-
3633
-		$mountPoint = $this->createMock(IMountPoint::class);
3634
-		$node->method('getMountPoint')
3635
-			->willReturn($mountPoint);
3636
-		$mountPoint->method('getStorageRootId')
3637
-			->willReturn(42);
3638
-
3639
-		$expected = new DataResponse([]);
3640
-		$result = $ocs->updateShare(42, null, null, null, null, '2010-12-23', null, null, null);
3641
-
3642
-		$this->assertInstanceOf(get_class($expected), $result);
3643
-		$this->assertEquals($expected->getData(), $result->getData());
3644
-	}
3645
-
3646
-	public function testUpdateLinkSharePublicUploadDoesNotChangeOther(): void {
3647
-		$ocs = $this->mockFormatShare();
3648
-
3649
-		$date = new \DateTime('2000-01-01');
3650
-
3651
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3652
-		$folder->method('getId')
3653
-			->willReturn(42);
3654
-
3655
-		$share = Server::get(IManager::class)->newShare();
3656
-		$share->setPermissions(Constants::PERMISSION_ALL)
3657
-			->setSharedBy($this->currentUser)
3658
-			->setShareType(IShare::TYPE_LINK)
3659
-			->setPassword('password')
3660
-			->setSendPasswordByTalk(true)
3661
-			->setExpirationDate($date)
3662
-			->setNote('note')
3663
-			->setLabel('label')
3664
-			->setHideDownload(true)
3665
-			->setPermissions(Constants::PERMISSION_ALL)
3666
-			->setNode($folder);
3667
-
3668
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3669
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3670
-
3671
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3672
-			$this->callback(function (IShare $share) use ($date) {
3673
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3674
-				&& $share->getPassword() === 'password'
3675
-				&& $share->getSendPasswordByTalk() === true
3676
-				&& $share->getExpirationDate() === $date
3677
-				&& $share->getNote() === 'note'
3678
-				&& $share->getLabel() === 'label'
3679
-				&& $share->getHideDownload() === true;
3680
-			})
3681
-		)->willReturnArgument(0);
3682
-
3683
-		$this->shareManager->method('getSharedWith')
3684
-			->willReturn([]);
3685
-
3686
-		$this->rootFolder->method('getUserFolder')
3687
-			->with($this->currentUser)
3688
-			->willReturn($userFolder);
3689
-
3690
-		$userFolder->method('getById')
3691
-			->with(42)
3692
-			->willReturn([$folder]);
3693
-
3694
-		$mountPoint = $this->createMock(IMountPoint::class);
3695
-		$folder->method('getMountPoint')
3696
-			->willReturn($mountPoint);
3697
-		$mountPoint->method('getStorageRootId')
3698
-			->willReturn(42);
3699
-
3700
-		$expected = new DataResponse([]);
3701
-		$result = $ocs->updateShare(42, null, null, null, 'true', null, null, null, null);
3702
-
3703
-		$this->assertInstanceOf(get_class($expected), $result);
3704
-		$this->assertEquals($expected->getData(), $result->getData());
3705
-	}
3706
-
3707
-	public function testUpdateLinkSharePermissions(): void {
3708
-		$ocs = $this->mockFormatShare();
3709
-
3710
-		$date = new \DateTime('2000-01-01');
3711
-
3712
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3713
-		$folder->method('getId')
3714
-			->willReturn(42);
3715
-
3716
-		$share = Server::get(IManager::class)->newShare();
3717
-		$share->setPermissions(Constants::PERMISSION_ALL)
3718
-			->setSharedBy($this->currentUser)
3719
-			->setShareType(IShare::TYPE_LINK)
3720
-			->setPassword('password')
3721
-			->setSendPasswordByTalk(true)
3722
-			->setExpirationDate($date)
3723
-			->setNote('note')
3724
-			->setLabel('label')
3725
-			->setHideDownload(true)
3726
-			->setPermissions(Constants::PERMISSION_ALL)
3727
-			->setNode($folder);
3728
-
3729
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3730
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3731
-
3732
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3733
-			$this->callback(function (IShare $share) use ($date): bool {
3734
-				return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3735
-				&& $share->getPassword() === 'password'
3736
-				&& $share->getSendPasswordByTalk() === true
3737
-				&& $share->getExpirationDate() === $date
3738
-				&& $share->getNote() === 'note'
3739
-				&& $share->getLabel() === 'label'
3740
-				&& $share->getHideDownload() === true;
3741
-			})
3742
-		)->willReturnArgument(0);
3743
-
3744
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3745
-
3746
-		$this->rootFolder->method('getUserFolder')
3747
-			->with($this->currentUser)
3748
-			->willReturn($userFolder);
3749
-
3750
-		$userFolder->method('getById')
3751
-			->with(42)
3752
-			->willReturn([$folder]);
3753
-
3754
-		$mountPoint = $this->createMock(IMountPoint::class);
3755
-		$folder->method('getMountPoint')
3756
-			->willReturn($mountPoint);
3757
-		$mountPoint->method('getStorageRootId')
3758
-			->willReturn(42);
3759
-
3760
-		$expected = new DataResponse([]);
3761
-		$result = $ocs->updateShare(42, 7, null, null, 'true', null, null, null, null);
3762
-
3763
-		$this->assertInstanceOf(get_class($expected), $result);
3764
-		$this->assertEquals($expected->getData(), $result->getData());
3765
-	}
3766
-
3767
-	public function testUpdateLinkSharePermissionsShare(): void {
3768
-		$ocs = $this->mockFormatShare();
3769
-
3770
-		$date = new \DateTime('2000-01-01');
3771
-
3772
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3773
-		$folder->method('getId')
3774
-			->willReturn(42);
3775
-
3776
-		$share = Server::get(IManager::class)->newShare();
3777
-		$share->setPermissions(Constants::PERMISSION_ALL)
3778
-			->setSharedBy($this->currentUser)
3779
-			->setShareType(IShare::TYPE_LINK)
3780
-			->setPassword('password')
3781
-			->setSendPasswordByTalk(true)
3782
-			->setExpirationDate($date)
3783
-			->setNote('note')
3784
-			->setLabel('label')
3785
-			->setHideDownload(true)
3786
-			->setPermissions(Constants::PERMISSION_READ)
3787
-			->setNode($folder);
3788
-
3789
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3790
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3791
-
3792
-		$this->shareManager->expects($this->once())
3793
-			->method('updateShare')
3794
-			->with(
3795
-				$this->callback(function (IShare $share) use ($date) {
3796
-					return $share->getPermissions() === Constants::PERMISSION_ALL
3797
-						&& $share->getPassword() === 'password'
3798
-						&& $share->getSendPasswordByTalk() === true
3799
-						&& $share->getExpirationDate() === $date
3800
-						&& $share->getNote() === 'note'
3801
-						&& $share->getLabel() === 'label'
3802
-						&& $share->getHideDownload() === true;
3803
-				})
3804
-			)->willReturnArgument(0);
3805
-
3806
-		$this->rootFolder->method('getUserFolder')
3807
-			->with($this->currentUser)
3808
-			->willReturn($userFolder);
3809
-
3810
-		$userFolder->method('getById')
3811
-			->with(42)
3812
-			->willReturn([$folder]);
3813
-
3814
-		$mountPoint = $this->createMock(IMountPoint::class);
3815
-		$folder->method('getMountPoint')
3816
-			->willReturn($mountPoint);
3817
-		$mountPoint->method('getStorageRootId')
3818
-			->willReturn(42);
3819
-
3820
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3821
-
3822
-		$expected = new DataResponse([]);
3823
-		$result = $ocs->updateShare(42, Constants::PERMISSION_ALL, null, null, null, null, null, null, null);
3824
-
3825
-		$this->assertInstanceOf(get_class($expected), $result);
3826
-		$this->assertEquals($expected->getData(), $result->getData());
3827
-	}
3828
-
3829
-	public function testUpdateOtherPermissions(): void {
3830
-		$ocs = $this->mockFormatShare();
3831
-
3832
-		[$userFolder, $file] = $this->getNonSharedUserFolder();
3833
-		$file->method('getId')
3834
-			->willReturn(42);
3835
-
3836
-		$share = Server::get(IManager::class)->newShare();
3837
-		$share->setPermissions(Constants::PERMISSION_ALL)
3838
-			->setSharedBy($this->currentUser)
3839
-			->setShareType(IShare::TYPE_USER)
3840
-			->setNode($file);
3841
-
3842
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3843
-		$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3844
-
3845
-		$this->shareManager->expects($this->once())->method('updateShare')->with(
3846
-			$this->callback(function (IShare $share) {
3847
-				return $share->getPermissions() === Constants::PERMISSION_ALL;
3848
-			})
3849
-		)->willReturnArgument(0);
3850
-
3851
-		$this->shareManager->method('getSharedWith')->willReturn([]);
3852
-
3853
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3854
-		$this->rootFolder->method('getUserFolder')
3855
-			->with($this->currentUser)
3856
-			->willReturn($userFolder);
3857
-
3858
-		$userFolder->method('getById')
3859
-			->with(42)
3860
-			->willReturn([$file]);
3861
-
3862
-		$mountPoint = $this->createMock(IMountPoint::class);
3863
-		$file->method('getMountPoint')
3864
-			->willReturn($mountPoint);
3865
-		$mountPoint->method('getStorageRootId')
3866
-			->willReturn(42);
3867
-
3868
-		$expected = new DataResponse([]);
3869
-		$result = $ocs->updateShare(42, 31, null, null, null, null);
3870
-
3871
-		$this->assertInstanceOf(get_class($expected), $result);
3872
-		$this->assertEquals($expected->getData(), $result->getData());
3873
-	}
3874
-
3875
-	public function testUpdateShareCannotIncreasePermissions(): void {
3876
-		$ocs = $this->mockFormatShare();
3877
-
3878
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3879
-		$folder->method('getId')
3880
-			->willReturn(42);
3881
-
3882
-		$share = Server::get(IManager::class)->newShare();
3883
-		$share
3884
-			->setId(42)
3885
-			->setSharedBy($this->currentUser)
3886
-			->setShareOwner('anotheruser')
3887
-			->setShareType(IShare::TYPE_GROUP)
3888
-			->setSharedWith('group1')
3889
-			->setPermissions(Constants::PERMISSION_READ)
3890
-			->setNode($folder);
3891
-
3892
-		// note: updateShare will modify the received instance but getSharedWith will reread from the database,
3893
-		// so their values will be different
3894
-		$incomingShare = Server::get(IManager::class)->newShare();
3895
-		$incomingShare
3896
-			->setId(42)
3897
-			->setSharedBy($this->currentUser)
3898
-			->setShareOwner('anotheruser')
3899
-			->setShareType(IShare::TYPE_GROUP)
3900
-			->setSharedWith('group1')
3901
-			->setPermissions(Constants::PERMISSION_READ)
3902
-			->setNode($folder);
3903
-
3904
-		$this->request
3905
-			->method('getParam')
3906
-			->willReturnMap([
3907
-				['permissions', null, '31'],
3908
-			]);
3909
-
3910
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3911
-
3912
-		$this->shareManager->expects($this->any())
3913
-			->method('getSharedWith')
3914
-			->willReturnMap([
3915
-				['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3916
-				['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]],
3917
-				['currentUser', IShare::TYPE_ROOM, $share->getNode(), -1, 0, []]
3918
-			]);
3919
-
3920
-		$this->rootFolder->method('getUserFolder')
3921
-			->with($this->currentUser)
3922
-			->willReturn($userFolder);
3923
-
3924
-		$userFolder->method('getById')
3925
-			->with(42)
3926
-			->willReturn([$folder]);
3927
-		$userFolder->method('getFirstNodeById')
3928
-			->with(42)
3929
-			->willReturn($folder);
3930
-
3931
-		$mountPoint = $this->createMock(IMountPoint::class);
3932
-		$folder->method('getMountPoint')
3933
-			->willReturn($mountPoint);
3934
-		$mountPoint->method('getStorageRootId')
3935
-			->willReturn(42);
3936
-
3937
-		$this->shareManager->expects($this->once())
3938
-			->method('updateShare')
3939
-			->with($share)
3940
-			->willThrowException(new GenericShareException('Cannot increase permissions of path/file', 'Cannot increase permissions of path/file', 404));
3941
-
3942
-		try {
3943
-			$ocs->updateShare(42, 31);
3944
-			$this->fail();
3945
-		} catch (OCSException $e) {
3946
-			$this->assertEquals('Cannot increase permissions of path/file', $e->getMessage());
3947
-		}
3948
-	}
3949
-
3950
-	public function testUpdateShareCanIncreasePermissionsIfOwner(): void {
3951
-		$ocs = $this->mockFormatShare();
3952
-
3953
-		[$userFolder, $folder] = $this->getNonSharedUserFolder();
3954
-		$folder->method('getId')
3955
-			->willReturn(42);
3956
-
3957
-		$share = Server::get(IManager::class)->newShare();
3958
-		$share
3959
-			->setId(42)
3960
-			->setSharedBy($this->currentUser)
3961
-			->setShareOwner($this->currentUser)
3962
-			->setShareType(IShare::TYPE_GROUP)
3963
-			->setSharedWith('group1')
3964
-			->setPermissions(Constants::PERMISSION_READ)
3965
-			->setNode($folder);
3966
-
3967
-		// note: updateShare will modify the received instance but getSharedWith will reread from the database,
3968
-		// so their values will be different
3969
-		$incomingShare = Server::get(IManager::class)->newShare();
3970
-		$incomingShare
3971
-			->setId(42)
3972
-			->setSharedBy($this->currentUser)
3973
-			->setShareOwner($this->currentUser)
3974
-			->setShareType(IShare::TYPE_GROUP)
3975
-			->setSharedWith('group1')
3976
-			->setPermissions(Constants::PERMISSION_READ)
3977
-			->setNode($folder);
3978
-
3979
-		$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3980
-
3981
-		$this->shareManager->expects($this->any())
3982
-			->method('getSharedWith')
3983
-			->willReturnMap([
3984
-				['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3985
-				['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]]
3986
-			]);
3987
-
3988
-		$this->shareManager->expects($this->once())
3989
-			->method('updateShare')
3990
-			->with($share)
3991
-			->willReturn($share);
3992
-
3993
-		$this->rootFolder->method('getUserFolder')
3994
-			->with($this->currentUser)
3995
-			->willReturn($userFolder);
3996
-
3997
-		$userFolder->method('getById')
3998
-			->with(42)
3999
-			->willReturn([$folder]);
4000
-
4001
-		$mountPoint = $this->createMock(IMountPoint::class);
4002
-		$folder->method('getMountPoint')
4003
-			->willReturn($mountPoint);
4004
-		$mountPoint->method('getStorageRootId')
4005
-			->willReturn(42);
4006
-
4007
-		$result = $ocs->updateShare(42, 31);
4008
-		$this->assertInstanceOf(DataResponse::class, $result);
4009
-	}
4010
-
4011
-	public function testUpdateShareOwnerless(): void {
4012
-		$ocs = $this->mockFormatShare();
4013
-
4014
-		$mount = $this->createMock(IShareOwnerlessMount::class);
4015
-
4016
-		$file = $this->createMock(File::class);
4017
-		$file
4018
-			->expects($this->exactly(2))
4019
-			->method('getPermissions')
4020
-			->willReturn(Constants::PERMISSION_SHARE);
4021
-		$file
4022
-			->expects($this->once())
4023
-			->method('getMountPoint')
4024
-			->willReturn($mount);
4025
-
4026
-		$userFolder = $this->createMock(Folder::class);
4027
-		$userFolder->method('getById')
4028
-			->with(2)
4029
-			->willReturn([$file]);
4030
-		$userFolder->method('getFirstNodeById')
4031
-			->with(2)
4032
-			->willReturn($file);
4033
-
4034
-		$this->rootFolder
4035
-			->method('getUserFolder')
4036
-			->with($this->currentUser)
4037
-			->willReturn($userFolder);
4038
-
4039
-		$share = $this->createMock(IShare::class);
4040
-		$share
4041
-			->expects($this->once())
4042
-			->method('getNode')
4043
-			->willReturn($file);
4044
-		$share
4045
-			->expects($this->exactly(2))
4046
-			->method('getNodeId')
4047
-			->willReturn(2);
4048
-		$share
4049
-			->expects($this->exactly(2))
4050
-			->method('getPermissions')
4051
-			->willReturn(Constants::PERMISSION_SHARE);
4052
-
4053
-		$this->shareManager
4054
-			->expects($this->once())
4055
-			->method('getShareById')
4056
-			->with('ocinternal:1', $this->currentUser)
4057
-			->willReturn($share);
4058
-
4059
-		$this->shareManager
4060
-			->expects($this->once())
4061
-			->method('updateShare')
4062
-			->with($share)
4063
-			->willReturn($share);
4064
-
4065
-		$result = $ocs->updateShare(1, Constants::PERMISSION_ALL);
4066
-		$this->assertInstanceOf(DataResponse::class, $result);
4067
-	}
4068
-
4069
-	public static function dataFormatShare(): array {
4070
-		$owner = ['getDisplayName' => 'ownerDN'];
4071
-		$initiator = ['getDisplayName' => 'initiatorDN'];
4072
-		$recipient = [
4073
-			'getDisplayName' => 'recipientDN',
4074
-			'getSystemEMailAddress' => 'recipient'
4075
-		];
4076
-
4077
-		$folder = [
4078
-			'class' => Folder::class,
4079
-			'mimeType' => 'myFolderMimeType',
4080
-			'path' => 'folder',
4081
-			'id' => 2,
4082
-		];
4083
-		$file = [
4084
-			'class' => File::class,
4085
-			'mimeType' => 'myMimeType',
4086
-			'path' => 'file',
4087
-			'id' => 3,
4088
-		];
4089
-		$fileWithPreview = [
4090
-			'class' => File::class,
4091
-			'mimeType' => 'mimeWithPreview',
4092
-			'path' => 'fileWithPreview',
4093
-			'id' => 4,
4094
-		];
4095
-
4096
-		$result = [];
4097
-
4098
-		$share = [
4099
-			'type' => IShare::TYPE_USER,
4100
-			'owner' => 'owner',
4101
-			'sharedWith' => 'recipient',
4102
-			'attributes' => [
4103
-				'scope' => 'permissions',
4104
-				'key' => 'download',
4105
-				'value' => true
4106
-			],
4107
-			'node' => $file,
4108
-			'note' => 'personal note',
4109
-		];
4110
-
4111
-		// User backend down
4112
-		$result[] = [
4113
-			[
4114
-				'id' => '42',
4115
-				'share_type' => IShare::TYPE_USER,
4116
-				'uid_owner' => 'initiator',
4117
-				'displayname_owner' => 'initiator',
4118
-				'permissions' => 1,
4119
-				'stime' => 946684862,
4120
-				'parent' => null,
4121
-				'expiration' => null,
4122
-				'token' => null,
4123
-				'uid_file_owner' => 'owner',
4124
-				'displayname_file_owner' => 'owner',
4125
-				'path' => 'file',
4126
-				'item_type' => 'file',
4127
-				'storage_id' => 'storageId',
4128
-				'storage' => 100,
4129
-				'item_source' => 3,
4130
-				'file_source' => 3,
4131
-				'file_parent' => 1,
4132
-				'file_target' => 'myTarget',
4133
-				'share_with' => 'recipient',
4134
-				'share_with_displayname' => 'recipient',
4135
-				'share_with_displayname_unique' => 'recipient',
4136
-				'note' => 'personal note',
4137
-				'label' => '',
4138
-				'mail_send' => 0,
4139
-				'mimetype' => 'myMimeType',
4140
-				'has_preview' => false,
4141
-				'hide_download' => 0,
4142
-				'can_edit' => false,
4143
-				'can_delete' => false,
4144
-				'item_size' => 123456,
4145
-				'item_mtime' => 1234567890,
4146
-				'is-mount-root' => false,
4147
-				'mount-type' => '',
4148
-				'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4149
-				'item_permissions' => 1,
4150
-			],
4151
-			$share,
4152
-			[], false
4153
-		];
4154
-		// User backend up
4155
-		$result[] = [
4156
-			[
4157
-				'id' => '42',
4158
-				'share_type' => IShare::TYPE_USER,
4159
-				'uid_owner' => 'initiator',
4160
-				'displayname_owner' => 'initiatorDN',
4161
-				'permissions' => 1,
4162
-				'stime' => 946684862,
4163
-				'parent' => null,
4164
-				'expiration' => null,
4165
-				'token' => null,
4166
-				'uid_file_owner' => 'owner',
4167
-				'displayname_file_owner' => 'ownerDN',
4168
-				'note' => 'personal note',
4169
-				'label' => '',
4170
-				'path' => 'file',
4171
-				'item_type' => 'file',
4172
-				'storage_id' => 'storageId',
4173
-				'storage' => 100,
4174
-				'item_source' => 3,
4175
-				'file_source' => 3,
4176
-				'file_parent' => 1,
4177
-				'file_target' => 'myTarget',
4178
-				'share_with' => 'recipient',
4179
-				'share_with_displayname' => 'recipientDN',
4180
-				'share_with_displayname_unique' => 'recipient',
4181
-				'mail_send' => 0,
4182
-				'mimetype' => 'myMimeType',
4183
-				'has_preview' => false,
4184
-				'hide_download' => 0,
4185
-				'can_edit' => false,
4186
-				'can_delete' => false,
4187
-				'item_size' => 123456,
4188
-				'item_mtime' => 1234567890,
4189
-				'is-mount-root' => false,
4190
-				'mount-type' => '',
4191
-				'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4192
-				'item_permissions' => 1,
4193
-			], $share, [
4194
-				['owner', $owner],
4195
-				['initiator', $initiator],
4196
-				['recipient', $recipient],
4197
-			], false
4198
-		];
4199
-
4200
-		// Same but no attributes
4201
-		$share = [
4202
-			'type' => IShare::TYPE_USER,
4203
-			'owner' => 'owner',
4204
-			'sharedWith' => 'recipient',
4205
-			'node' => $file,
4206
-			'note' => 'personal note',
4207
-		];
4208
-
4209
-		// User backend down
4210
-		$result[] = [
4211
-			[
4212
-				'id' => '42',
4213
-				'share_type' => IShare::TYPE_USER,
4214
-				'uid_owner' => 'initiator',
4215
-				'displayname_owner' => 'initiator',
4216
-				'permissions' => 1,
4217
-				'attributes' => null,
4218
-				'stime' => 946684862,
4219
-				'parent' => null,
4220
-				'expiration' => null,
4221
-				'token' => null,
4222
-				'uid_file_owner' => 'owner',
4223
-				'displayname_file_owner' => 'owner',
4224
-				'note' => 'personal note',
4225
-				'label' => '',
4226
-				'path' => 'file',
4227
-				'item_type' => 'file',
4228
-				'storage_id' => 'storageId',
4229
-				'storage' => 100,
4230
-				'item_source' => 3,
4231
-				'file_source' => 3,
4232
-				'file_parent' => 1,
4233
-				'file_target' => 'myTarget',
4234
-				'share_with' => 'recipient',
4235
-				'share_with_displayname' => 'recipient',
4236
-				'share_with_displayname_unique' => 'recipient',
4237
-				'mail_send' => 0,
4238
-				'mimetype' => 'myMimeType',
4239
-				'has_preview' => false,
4240
-				'hide_download' => 0,
4241
-				'can_edit' => false,
4242
-				'can_delete' => false,
4243
-				'item_size' => 123456,
4244
-				'item_mtime' => 1234567890,
4245
-				'is-mount-root' => false,
4246
-				'mount-type' => '',
4247
-				'attributes' => null,
4248
-				'item_permissions' => 1,
4249
-			], $share, [], false
4250
-		];
4251
-
4252
-		$share['owner'] = 'currentUser';
4253
-
4254
-		// User backend down
4255
-		$result[] = [
4256
-			[
4257
-				'id' => '42',
4258
-				'share_type' => IShare::TYPE_USER,
4259
-				'uid_owner' => 'initiator',
4260
-				'displayname_owner' => 'initiator',
4261
-				'permissions' => 1,
4262
-				'attributes' => null,
4263
-				'stime' => 946684862,
4264
-				'parent' => null,
4265
-				'expiration' => null,
4266
-				'token' => null,
4267
-				'uid_file_owner' => 'currentUser',
4268
-				'displayname_file_owner' => 'currentUser',
4269
-				'note' => 'personal note',
4270
-				'label' => '',
4271
-				'path' => 'file',
4272
-				'item_type' => 'file',
4273
-				'storage_id' => 'storageId',
4274
-				'storage' => 100,
4275
-				'item_source' => 3,
4276
-				'file_source' => 3,
4277
-				'file_parent' => 1,
4278
-				'file_target' => 'myTarget',
4279
-				'share_with' => 'recipient',
4280
-				'share_with_displayname' => 'recipient',
4281
-				'share_with_displayname_unique' => 'recipient',
4282
-				'mail_send' => 0,
4283
-				'mimetype' => 'myMimeType',
4284
-				'has_preview' => false,
4285
-				'hide_download' => 0,
4286
-				'can_edit' => true,
4287
-				'can_delete' => true,
4288
-				'item_size' => 123456,
4289
-				'item_mtime' => 1234567890,
4290
-				'is-mount-root' => false,
4291
-				'mount-type' => '',
4292
-				'attributes' => null,
4293
-				'item_permissions' => 11,
4294
-			], $share, [], false
4295
-		];
4296
-
4297
-		// with existing group
4298
-		$share = [
4299
-			'type' => IShare::TYPE_GROUP,
4300
-			'owner' => 'owner',
4301
-			'sharedWith' => 'recipientGroup',
4302
-			'node' => $file,
4303
-			'note' => 'personal note',
4304
-		];
4305
-
4306
-		$result[] = [
4307
-			[
4308
-				'id' => '42',
4309
-				'share_type' => IShare::TYPE_GROUP,
4310
-				'uid_owner' => 'initiator',
4311
-				'displayname_owner' => 'initiator',
4312
-				'permissions' => 1,
4313
-				'attributes' => null,
4314
-				'stime' => 946684862,
4315
-				'parent' => null,
4316
-				'expiration' => null,
4317
-				'token' => null,
4318
-				'uid_file_owner' => 'owner',
4319
-				'displayname_file_owner' => 'owner',
4320
-				'note' => 'personal note',
4321
-				'label' => '',
4322
-				'path' => 'file',
4323
-				'item_type' => 'file',
4324
-				'storage_id' => 'storageId',
4325
-				'storage' => 100,
4326
-				'item_source' => 3,
4327
-				'file_source' => 3,
4328
-				'file_parent' => 1,
4329
-				'file_target' => 'myTarget',
4330
-				'share_with' => 'recipientGroup',
4331
-				'share_with_displayname' => 'recipientGroupDisplayName',
4332
-				'mail_send' => 0,
4333
-				'mimetype' => 'myMimeType',
4334
-				'has_preview' => false,
4335
-				'hide_download' => 0,
4336
-				'can_edit' => false,
4337
-				'can_delete' => false,
4338
-				'item_size' => 123456,
4339
-				'item_mtime' => 1234567890,
4340
-				'is-mount-root' => false,
4341
-				'mount-type' => '',
4342
-				'attributes' => null,
4343
-				'item_permissions' => 1,
4344
-			], $share, [], false
4345
-		];
4346
-
4347
-		// with unknown group / no group backend
4348
-		$share['sharedWith'] = 'recipientGroup2';
4349
-
4350
-		$result[] = [
4351
-			[
4352
-				'id' => '42',
4353
-				'share_type' => IShare::TYPE_GROUP,
4354
-				'uid_owner' => 'initiator',
4355
-				'displayname_owner' => 'initiator',
4356
-				'permissions' => 1,
4357
-				'stime' => 946684862,
4358
-				'parent' => null,
4359
-				'expiration' => null,
4360
-				'token' => null,
4361
-				'uid_file_owner' => 'owner',
4362
-				'displayname_file_owner' => 'owner',
4363
-				'note' => 'personal note',
4364
-				'label' => '',
4365
-				'path' => 'file',
4366
-				'item_type' => 'file',
4367
-				'storage_id' => 'storageId',
4368
-				'storage' => 100,
4369
-				'item_source' => 3,
4370
-				'file_source' => 3,
4371
-				'file_parent' => 1,
4372
-				'file_target' => 'myTarget',
4373
-				'share_with' => 'recipientGroup2',
4374
-				'share_with_displayname' => 'recipientGroup2',
4375
-				'mail_send' => 0,
4376
-				'mimetype' => 'myMimeType',
4377
-				'has_preview' => false,
4378
-				'hide_download' => 0,
4379
-				'can_edit' => false,
4380
-				'can_delete' => false,
4381
-				'item_size' => 123456,
4382
-				'item_mtime' => 1234567890,
4383
-				'is-mount-root' => false,
4384
-				'mount-type' => '',
4385
-				'attributes' => null,
4386
-				'item_permissions' => 1,
4387
-			], $share, [], false
4388
-		];
4389
-
4390
-		$share = [
4391
-			'type' => IShare::TYPE_LINK,
4392
-			'owner' => 'owner',
4393
-			'node' => $file,
4394
-			'note' => 'personal note',
4395
-			'password' => 'mypassword',
4396
-			'expirationDate' => new \DateTime('2001-01-02T00:00:00'),
4397
-			'token' => 'myToken',
4398
-			'label' => 'new link share',
4399
-		];
4400
-
4401
-		$result[] = [
4402
-			[
4403
-				'id' => '42',
4404
-				'share_type' => IShare::TYPE_LINK,
4405
-				'uid_owner' => 'initiator',
4406
-				'displayname_owner' => 'initiator',
4407
-				'permissions' => 1,
4408
-				'attributes' => null,
4409
-				'stime' => 946684862,
4410
-				'parent' => null,
4411
-				'expiration' => '2001-01-02 00:00:00',
4412
-				'token' => 'myToken',
4413
-				'uid_file_owner' => 'owner',
4414
-				'displayname_file_owner' => 'owner',
4415
-				'note' => 'personal note',
4416
-				'label' => 'new link share',
4417
-				'path' => 'file',
4418
-				'item_type' => 'file',
4419
-				'storage_id' => 'storageId',
4420
-				'storage' => 100,
4421
-				'item_source' => 3,
4422
-				'file_source' => 3,
4423
-				'file_parent' => 1,
4424
-				'file_target' => 'myTarget',
4425
-				'password' => 'mypassword',
4426
-				'share_with' => 'mypassword',
4427
-				'share_with_displayname' => '(Shared link)',
4428
-				'send_password_by_talk' => false,
4429
-				'mail_send' => 0,
4430
-				'url' => 'myLink',
4431
-				'mimetype' => 'myMimeType',
4432
-				'has_preview' => false,
4433
-				'hide_download' => 0,
4434
-				'can_edit' => false,
4435
-				'can_delete' => false,
4436
-				'item_size' => 123456,
4437
-				'item_mtime' => 1234567890,
4438
-				'is-mount-root' => false,
4439
-				'mount-type' => '',
4440
-				'attributes' => null,
4441
-				'item_permissions' => 1,
4442
-			], $share, [], false
4443
-		];
4444
-
4445
-		$share['sendPasswordByTalk'] = true;
4446
-
4447
-		$result[] = [
4448
-			[
4449
-				'id' => '42',
4450
-				'share_type' => IShare::TYPE_LINK,
4451
-				'uid_owner' => 'initiator',
4452
-				'displayname_owner' => 'initiator',
4453
-				'permissions' => 1,
4454
-				'stime' => 946684862,
4455
-				'parent' => null,
4456
-				'expiration' => '2001-01-02 00:00:00',
4457
-				'token' => 'myToken',
4458
-				'uid_file_owner' => 'owner',
4459
-				'displayname_file_owner' => 'owner',
4460
-				'note' => 'personal note',
4461
-				'label' => 'new link share',
4462
-				'path' => 'file',
4463
-				'item_type' => 'file',
4464
-				'storage_id' => 'storageId',
4465
-				'storage' => 100,
4466
-				'item_source' => 3,
4467
-				'file_source' => 3,
4468
-				'file_parent' => 1,
4469
-				'file_target' => 'myTarget',
4470
-				'password' => 'mypassword',
4471
-				'share_with' => 'mypassword',
4472
-				'share_with_displayname' => '(Shared link)',
4473
-				'send_password_by_talk' => true,
4474
-				'mail_send' => 0,
4475
-				'url' => 'myLink',
4476
-				'mimetype' => 'myMimeType',
4477
-				'has_preview' => false,
4478
-				'hide_download' => 0,
4479
-				'can_edit' => false,
4480
-				'can_delete' => false,
4481
-				'item_size' => 123456,
4482
-				'item_mtime' => 1234567890,
4483
-				'is-mount-root' => false,
4484
-				'mount-type' => '',
4485
-				'attributes' => null,
4486
-				'item_permissions' => 1,
4487
-			], $share, [], false
4488
-		];
4489
-
4490
-		$share = [
4491
-			'type' => IShare::TYPE_REMOTE,
4492
-			'owner' => 'owner',
4493
-			'sharedWith' => '[email protected]',
4494
-			'node' => $folder,
4495
-			'note' => 'personal note',
4496
-			'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4497
-		];
4498
-
4499
-		$result[] = [
4500
-			[
4501
-				'id' => '42',
4502
-				'share_type' => IShare::TYPE_REMOTE,
4503
-				'uid_owner' => 'initiator',
4504
-				'displayname_owner' => 'initiator',
4505
-				'permissions' => 1,
4506
-				'stime' => 946684862,
4507
-				'parent' => null,
4508
-				'expiration' => '2001-02-03 00:00:00',
4509
-				'token' => null,
4510
-				'uid_file_owner' => 'owner',
4511
-				'displayname_file_owner' => 'owner',
4512
-				'note' => 'personal note',
4513
-				'label' => '',
4514
-				'path' => 'folder',
4515
-				'item_type' => 'folder',
4516
-				'storage_id' => 'storageId',
4517
-				'storage' => 100,
4518
-				'item_source' => 2,
4519
-				'file_source' => 2,
4520
-				'file_parent' => 1,
4521
-				'file_target' => 'myTarget',
4522
-				'share_with' => '[email protected]',
4523
-				'share_with_displayname' => 'foobar',
4524
-				'mail_send' => 0,
4525
-				'mimetype' => 'myFolderMimeType',
4526
-				'has_preview' => false,
4527
-				'hide_download' => 0,
4528
-				'can_edit' => false,
4529
-				'can_delete' => false,
4530
-				'item_size' => 123456,
4531
-				'item_mtime' => 1234567890,
4532
-				'is-mount-root' => false,
4533
-				'mount-type' => '',
4534
-				'attributes' => null,
4535
-				'item_permissions' => 1,
4536
-				'is_trusted_server' => false,
4537
-			], $share, [], false
4538
-		];
4539
-
4540
-		$share = [
4541
-			'type' => IShare::TYPE_REMOTE_GROUP,
4542
-			'owner' => 'owner',
4543
-			'sharedWith' => '[email protected]',
4544
-			'node' => $folder,
4545
-			'note' => 'personal note',
4546
-			'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4547
-		];
4548
-
4549
-		$result[] = [
4550
-			[
4551
-				'id' => '42',
4552
-				'share_type' => IShare::TYPE_REMOTE_GROUP,
4553
-				'uid_owner' => 'initiator',
4554
-				'displayname_owner' => 'initiator',
4555
-				'permissions' => 1,
4556
-				'stime' => 946684862,
4557
-				'parent' => null,
4558
-				'expiration' => '2001-02-03 00:00:00',
4559
-				'token' => null,
4560
-				'uid_file_owner' => 'owner',
4561
-				'displayname_file_owner' => 'owner',
4562
-				'note' => 'personal note',
4563
-				'label' => '',
4564
-				'path' => 'folder',
4565
-				'item_type' => 'folder',
4566
-				'storage_id' => 'storageId',
4567
-				'storage' => 100,
4568
-				'item_source' => 2,
4569
-				'file_source' => 2,
4570
-				'file_parent' => 1,
4571
-				'file_target' => 'myTarget',
4572
-				'share_with' => '[email protected]',
4573
-				'share_with_displayname' => 'foobar',
4574
-				'mail_send' => 0,
4575
-				'mimetype' => 'myFolderMimeType',
4576
-				'has_preview' => false,
4577
-				'hide_download' => 0,
4578
-				'can_edit' => false,
4579
-				'can_delete' => false,
4580
-				'item_size' => 123456,
4581
-				'item_mtime' => 1234567890,
4582
-				'is-mount-root' => false,
4583
-				'mount-type' => '',
4584
-				'attributes' => null,
4585
-				'item_permissions' => 1,
4586
-				'is_trusted_server' => false,
4587
-			], $share, [], false
4588
-		];
4589
-
4590
-		// Circle with id, display name and avatar set by the Circles app
4591
-		$share = [
4592
-			'type' => IShare::TYPE_CIRCLE,
4593
-			'owner' => 'owner',
4594
-			'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4595
-			'sharedWithDisplayName' => 'The display name',
4596
-			'sharedWithAvatar' => 'path/to/the/avatar',
4597
-			'node' => $folder,
4598
-		];
4599
-
4600
-		$result[] = [
4601
-			[
4602
-				'id' => '42',
4603
-				'share_type' => IShare::TYPE_CIRCLE,
4604
-				'uid_owner' => 'initiator',
4605
-				'displayname_owner' => 'initiator',
4606
-				'permissions' => 1,
4607
-				'attributes' => null,
4608
-				'stime' => 946684862,
4609
-				'parent' => null,
4610
-				'expiration' => null,
4611
-				'token' => null,
4612
-				'uid_file_owner' => 'owner',
4613
-				'displayname_file_owner' => 'owner',
4614
-				'note' => '',
4615
-				'label' => '',
4616
-				'path' => 'folder',
4617
-				'item_type' => 'folder',
4618
-				'storage_id' => 'storageId',
4619
-				'storage' => 100,
4620
-				'item_source' => 2,
4621
-				'file_source' => 2,
4622
-				'file_parent' => 1,
4623
-				'file_target' => 'myTarget',
4624
-				'share_with' => '4815162342',
4625
-				'share_with_displayname' => 'The display name',
4626
-				'share_with_avatar' => 'path/to/the/avatar',
4627
-				'mail_send' => 0,
4628
-				'mimetype' => 'myFolderMimeType',
4629
-				'has_preview' => false,
4630
-				'hide_download' => 0,
4631
-				'can_edit' => false,
4632
-				'can_delete' => false,
4633
-				'item_size' => 123456,
4634
-				'item_mtime' => 1234567890,
4635
-				'is-mount-root' => false,
4636
-				'mount-type' => '',
4637
-				'attributes' => null,
4638
-				'item_permissions' => 1,
4639
-			], $share, [], false
4640
-		];
4641
-
4642
-		// Circle with id set by the Circles app
4643
-		$share = [
4644
-			'type' => IShare::TYPE_CIRCLE,
4645
-			'owner' => 'owner',
4646
-			'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4647
-			'node' => $folder,
4648
-		];
4649
-
4650
-		$result[] = [
4651
-			[
4652
-				'id' => '42',
4653
-				'share_type' => IShare::TYPE_CIRCLE,
4654
-				'uid_owner' => 'initiator',
4655
-				'displayname_owner' => 'initiator',
4656
-				'permissions' => 1,
4657
-				'stime' => 946684862,
4658
-				'parent' => null,
4659
-				'expiration' => null,
4660
-				'token' => null,
4661
-				'uid_file_owner' => 'owner',
4662
-				'displayname_file_owner' => 'owner',
4663
-				'note' => '',
4664
-				'label' => '',
4665
-				'path' => 'folder',
4666
-				'item_type' => 'folder',
4667
-				'storage_id' => 'storageId',
4668
-				'storage' => 100,
4669
-				'item_source' => 2,
4670
-				'file_source' => 2,
4671
-				'file_parent' => 1,
4672
-				'file_target' => 'myTarget',
4673
-				'share_with' => '4815162342',
4674
-				'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4675
-				'share_with_avatar' => '',
4676
-				'mail_send' => 0,
4677
-				'mimetype' => 'myFolderMimeType',
4678
-				'has_preview' => false,
4679
-				'hide_download' => 0,
4680
-				'can_edit' => false,
4681
-				'can_delete' => false,
4682
-				'item_size' => 123456,
4683
-				'item_mtime' => 1234567890,
4684
-				'is-mount-root' => false,
4685
-				'mount-type' => '',
4686
-				'attributes' => null,
4687
-				'item_permissions' => 1,
4688
-			], $share, [], false
4689
-		];
4690
-
4691
-		// Circle with id not set by the Circles app
4692
-		$share = [
4693
-			'type' => IShare::TYPE_CIRCLE,
4694
-			'owner' => 'owner',
4695
-			'sharedWith' => 'Circle (Public circle, circleOwner)',
4696
-			'node' => $folder,
4697
-		];
4698
-
4699
-		$result[] = [
4700
-			[
4701
-				'id' => '42',
4702
-				'share_type' => IShare::TYPE_CIRCLE,
4703
-				'uid_owner' => 'initiator',
4704
-				'displayname_owner' => 'initiator',
4705
-				'permissions' => 1,
4706
-				'stime' => 946684862,
4707
-				'parent' => null,
4708
-				'expiration' => null,
4709
-				'token' => null,
4710
-				'uid_file_owner' => 'owner',
4711
-				'displayname_file_owner' => 'owner',
4712
-				'note' => '',
4713
-				'label' => '',
4714
-				'path' => 'folder',
4715
-				'item_type' => 'folder',
4716
-				'storage_id' => 'storageId',
4717
-				'storage' => 100,
4718
-				'item_source' => 2,
4719
-				'file_source' => 2,
4720
-				'file_parent' => 1,
4721
-				'file_target' => 'myTarget',
4722
-				'share_with' => 'Circle',
4723
-				'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4724
-				'share_with_avatar' => '',
4725
-				'mail_send' => 0,
4726
-				'mimetype' => 'myFolderMimeType',
4727
-				'has_preview' => false,
4728
-				'hide_download' => 0,
4729
-				'can_edit' => false,
4730
-				'can_delete' => false,
4731
-				'item_size' => 123456,
4732
-				'item_mtime' => 1234567890,
4733
-				'is-mount-root' => false,
4734
-				'mount-type' => '',
4735
-				'attributes' => null,
4736
-				'item_permissions' => 1,
4737
-			], $share, [], false
4738
-		];
4739
-
4740
-		// No node
4741
-		$share = [
4742
-			'type' => IShare::TYPE_USER,
4743
-			'owner' => 'owner',
4744
-			'sharedWith' => 'recipient',
4745
-			'note' => 'personal note',
4746
-		];
4747
-
4748
-		$result[] = [
4749
-			[], $share, [], true
4750
-		];
4751
-
4752
-		$share = [
4753
-			'type' => IShare::TYPE_EMAIL,
4754
-			'owner' => 'owner',
4755
-			'sharedWith' => '[email protected]',
4756
-			'node' => $folder,
4757
-			'password' => 'password',
4758
-		];
4759
-
4760
-		$result[] = [
4761
-			[
4762
-				'id' => '42',
4763
-				'share_type' => IShare::TYPE_EMAIL,
4764
-				'uid_owner' => 'initiator',
4765
-				'displayname_owner' => 'initiator',
4766
-				'permissions' => 1,
4767
-				'stime' => 946684862,
4768
-				'parent' => null,
4769
-				'expiration' => null,
4770
-				'token' => null,
4771
-				'uid_file_owner' => 'owner',
4772
-				'displayname_file_owner' => 'owner',
4773
-				'note' => '',
4774
-				'label' => '',
4775
-				'path' => 'folder',
4776
-				'item_type' => 'folder',
4777
-				'storage_id' => 'storageId',
4778
-				'storage' => 100,
4779
-				'item_source' => 2,
4780
-				'file_source' => 2,
4781
-				'file_parent' => 1,
4782
-				'file_target' => 'myTarget',
4783
-				'share_with' => '[email protected]',
4784
-				'share_with_displayname' => 'mail display name',
4785
-				'mail_send' => 0,
4786
-				'mimetype' => 'myFolderMimeType',
4787
-				'has_preview' => false,
4788
-				'password' => 'password',
4789
-				'send_password_by_talk' => false,
4790
-				'hide_download' => 0,
4791
-				'can_edit' => false,
4792
-				'can_delete' => false,
4793
-				'password_expiration_time' => null,
4794
-				'item_size' => 123456,
4795
-				'item_mtime' => 1234567890,
4796
-				'is-mount-root' => false,
4797
-				'mount-type' => '',
4798
-				'attributes' => null,
4799
-				'item_permissions' => 1,
4800
-			], $share, [], false
4801
-		];
4802
-
4803
-		$share['sendPasswordByTalk'] = true;
4804
-
4805
-		$result[] = [
4806
-			[
4807
-				'id' => '42',
4808
-				'share_type' => IShare::TYPE_EMAIL,
4809
-				'uid_owner' => 'initiator',
4810
-				'displayname_owner' => 'initiator',
4811
-				'permissions' => 1,
4812
-				'stime' => 946684862,
4813
-				'parent' => null,
4814
-				'expiration' => null,
4815
-				'token' => null,
4816
-				'uid_file_owner' => 'owner',
4817
-				'displayname_file_owner' => 'owner',
4818
-				'note' => '',
4819
-				'label' => '',
4820
-				'path' => 'folder',
4821
-				'item_type' => 'folder',
4822
-				'storage_id' => 'storageId',
4823
-				'storage' => 100,
4824
-				'item_source' => 2,
4825
-				'file_source' => 2,
4826
-				'file_parent' => 1,
4827
-				'file_target' => 'myTarget',
4828
-				'share_with' => '[email protected]',
4829
-				'share_with_displayname' => 'mail display name',
4830
-				'mail_send' => 0,
4831
-				'mimetype' => 'myFolderMimeType',
4832
-				'has_preview' => false,
4833
-				'password' => 'password',
4834
-				'send_password_by_talk' => true,
4835
-				'hide_download' => 0,
4836
-				'can_edit' => false,
4837
-				'can_delete' => false,
4838
-				'password_expiration_time' => null,
4839
-				'item_size' => 123456,
4840
-				'item_mtime' => 1234567890,
4841
-				'is-mount-root' => false,
4842
-				'mount-type' => '',
4843
-				'attributes' => null,
4844
-				'item_permissions' => 1,
4845
-			], $share, [], false
4846
-		];
4847
-
4848
-		// Preview is available
4849
-		$share = [
4850
-			'type' => IShare::TYPE_USER,
4851
-			'owner' => 'currentUser',
4852
-			'sharedWith' => 'recipient',
4853
-			'node' => $fileWithPreview,
4854
-			'note' => 'personal note',
4855
-		];
4856
-
4857
-		$result[] = [
4858
-			[
4859
-				'id' => '42',
4860
-				'share_type' => IShare::TYPE_USER,
4861
-				'uid_owner' => 'initiator',
4862
-				'displayname_owner' => 'initiator',
4863
-				'permissions' => 1,
4864
-				'stime' => 946684862,
4865
-				'parent' => null,
4866
-				'expiration' => null,
4867
-				'token' => null,
4868
-				'uid_file_owner' => 'currentUser',
4869
-				'displayname_file_owner' => 'currentUser',
4870
-				'note' => 'personal note',
4871
-				'label' => '',
4872
-				'path' => 'fileWithPreview',
4873
-				'item_type' => 'file',
4874
-				'storage_id' => 'storageId',
4875
-				'storage' => 100,
4876
-				'item_source' => 4,
4877
-				'file_source' => 4,
4878
-				'file_parent' => 1,
4879
-				'file_target' => 'myTarget',
4880
-				'share_with' => 'recipient',
4881
-				'share_with_displayname' => 'recipient',
4882
-				'share_with_displayname_unique' => 'recipient',
4883
-				'mail_send' => 0,
4884
-				'mimetype' => 'mimeWithPreview',
4885
-				'has_preview' => true,
4886
-				'hide_download' => 0,
4887
-				'can_edit' => true,
4888
-				'can_delete' => true,
4889
-				'item_size' => 123456,
4890
-				'item_mtime' => 1234567890,
4891
-				'is-mount-root' => false,
4892
-				'mount-type' => '',
4893
-				'attributes' => null,
4894
-				'item_permissions' => 11,
4895
-			], $share, [], false
4896
-		];
4897
-
4898
-		return $result;
4899
-	}
4900
-
4901
-	#[DataProvider('dataFormatShare')]
4902
-	public function testFormatShare(
4903
-		array $expects,
4904
-		array $shareParams,
4905
-		array $users,
4906
-		bool $exception,
4907
-	): void {
4908
-		$users = array_map(
4909
-			function ($user) {
4910
-				$mock = $this->createMock(IUser::class);
4911
-				foreach ($user[1] as $method => $return) {
4912
-					$mock->method($method)->willReturn($return);
4913
-				}
4914
-				return [$user[0],$mock];
4915
-			},
4916
-			$users
4917
-		);
4918
-
4919
-		$share = Server::get(IManager::class)->newShare();
4920
-		$share->setShareType($shareParams['type'])
4921
-			->setSharedBy('initiator')
4922
-			->setShareOwner($shareParams['owner'])
4923
-			->setPermissions(Constants::PERMISSION_READ)
4924
-			->setShareTime(new \DateTime('2000-01-01T00:01:02'))
4925
-			->setTarget('myTarget')
4926
-			->setId(42);
4927
-		if (isset($shareParams['sharedWith'])) {
4928
-			$share->setSharedWith($shareParams['sharedWith']);
4929
-		}
4930
-		if (isset($shareParams['sharedWithDisplayName'])) {
4931
-			$share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
4932
-		}
4933
-		if (isset($shareParams['sharedWithAvatar'])) {
4934
-			$share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
4935
-		}
4936
-		if (isset($shareParams['attributes'])) {
4937
-			$shareAttributes = $this->createMock(IShareAttributes::class);
4938
-			$shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
4939
-			$shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
4940
-			$share->setAttributes($shareAttributes);
4941
-
4942
-			$expects['attributes'] = \json_encode($shareParams['attributes']);
4943
-		}
4944
-		if (isset($shareParams['node'])) {
4945
-			$node = $this->createMock($shareParams['node']['class']);
4946
-
4947
-			$node->method('getMimeType')->willReturn($shareParams['node']['mimeType']);
4948
-
4949
-			$mountPoint = $this->createMock(IMountPoint::class);
4950
-			$mountPoint->method('getMountType')->willReturn('');
4951
-			$node->method('getMountPoint')->willReturn($mountPoint);
4952
-
4953
-			$node->method('getPath')->willReturn($shareParams['node']['path']);
4954
-			$node->method('getId')->willReturn($shareParams['node']['id']);
4955
-
4956
-			$parent = $this->createMock(Folder::class);
4957
-			$parent->method('getId')->willReturn(1);
4958
-			$node->method('getParent')->willReturn($parent);
4959
-
4960
-			$node->method('getSize')->willReturn(123456);
4961
-			$node->method('getMTime')->willReturn(1234567890);
4962
-
4963
-			$cache = $this->createMock(ICache::class);
4964
-			$cache->method('getNumericStorageId')->willReturn(100);
4965
-			$storage = $this->createMock(IStorage::class);
4966
-			$storage->method('getId')->willReturn('storageId');
4967
-			$storage->method('getCache')->willReturn($cache);
4968
-
4969
-			$node->method('getStorage')->willReturn($storage);
4970
-
4971
-			$share->setNode($node);
4972
-		}
4973
-		if (isset($shareParams['note'])) {
4974
-			$share->setNote($shareParams['note']);
4975
-		}
4976
-		if (isset($shareParams['expirationDate'])) {
4977
-			$share->setExpirationDate($shareParams['expirationDate']);
4978
-		}
4979
-		if (isset($shareParams['token'])) {
4980
-			$share->setToken($shareParams['token']);
4981
-		}
4982
-		if (isset($shareParams['label'])) {
4983
-			$share->setLabel($shareParams['label']);
4984
-		}
4985
-		if (isset($shareParams['password'])) {
4986
-			$share->setPassword($shareParams['password']);
4987
-		}
4988
-		if (isset($shareParams['sendPasswordByTalk'])) {
4989
-			$share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
4990
-		}
4991
-
4992
-		$this->userManager->method('get')->willReturnMap($users);
4993
-
4994
-		$recipientGroup = $this->createMock(IGroup::class);
4995
-		$recipientGroup->method('getDisplayName')->willReturn('recipientGroupDisplayName');
4996
-		$this->groupManager->method('get')->willReturnMap([
4997
-			['recipientGroup', $recipientGroup],
4998
-		]);
4999
-
5000
-		$this->urlGenerator->method('linkToRouteAbsolute')
5001
-			->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken'])
5002
-			->willReturn('myLink');
5003
-
5004
-		$this->rootFolder->method('getUserFolder')
5005
-			->with($this->currentUser)
5006
-			->willReturnSelf();
5007
-		$this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
5008
-
5009
-		if (!$exception) {
5010
-			$this->rootFolder->method('getFirstNodeById')
5011
-				->with($share->getNodeId())
5012
-				->willReturn($share->getNode());
5013
-
5014
-			$this->rootFolder->method('getRelativePath')
5015
-				->with($share->getNode()->getPath())
5016
-				->willReturnArgument(0);
5017
-		}
5018
-
5019
-		$cm = $this->createMock(\OCP\Contacts\IManager::class);
5020
-		$this->overwriteService(\OCP\Contacts\IManager::class, $cm);
5021
-
5022
-		$cm->method('search')
5023
-			->willReturnMap([
5024
-				['[email protected]', ['CLOUD'], [
5025
-					'limit' => 1,
5026
-					'enumeration' => false,
5027
-					'strict_search' => true,
5028
-				],
5029
-					[
5030
-						[
5031
-							'CLOUD' => [
5032
-								'[email protected]',
5033
-							],
5034
-							'FN' => 'foobar',
5035
-						],
5036
-					],
5037
-				],
5038
-				['[email protected]', ['EMAIL'], [
5039
-					'limit' => 1,
5040
-					'enumeration' => false,
5041
-					'strict_search' => true,
5042
-				],
5043
-					[
5044
-						[
5045
-							'EMAIL' => [
5046
-								'[email protected]',
5047
-							],
5048
-							'FN' => 'mail display name',
5049
-						],
5050
-					],
5051
-				],
5052
-			]);
5053
-
5054
-		try {
5055
-			$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5056
-			$this->assertFalse($exception);
5057
-			$this->assertEquals($expects, $result);
5058
-		} catch (NotFoundException $e) {
5059
-			$this->assertTrue($exception);
5060
-		}
5061
-	}
5062
-
5063
-	public static function dataFormatRoomShare(): array {
5064
-		$result = [];
5065
-
5066
-		$result[] = [
5067
-			[
5068
-				'id' => '42',
5069
-				'share_type' => IShare::TYPE_ROOM,
5070
-				'uid_owner' => 'initiator',
5071
-				'displayname_owner' => 'initiator',
5072
-				'permissions' => 1,
5073
-				'stime' => 946684862,
5074
-				'parent' => null,
5075
-				'expiration' => null,
5076
-				'token' => null,
5077
-				'uid_file_owner' => 'owner',
5078
-				'displayname_file_owner' => 'owner',
5079
-				'note' => 'personal note',
5080
-				'path' => 'file',
5081
-				'item_type' => 'file',
5082
-				'storage_id' => 'storageId',
5083
-				'storage' => 100,
5084
-				'item_source' => 3,
5085
-				'file_source' => 3,
5086
-				'file_parent' => 1,
5087
-				'file_target' => 'myTarget',
5088
-				'share_with' => 'recipientRoom',
5089
-				'share_with_displayname' => '',
5090
-				'mail_send' => 0,
5091
-				'mimetype' => 'myMimeType',
5092
-				'has_preview' => false,
5093
-				'hide_download' => 0,
5094
-				'label' => '',
5095
-				'can_edit' => false,
5096
-				'can_delete' => false,
5097
-				'item_size' => 123456,
5098
-				'item_mtime' => 1234567890,
5099
-				'is-mount-root' => false,
5100
-				'mount-type' => '',
5101
-				'attributes' => null,
5102
-				'item_permissions' => 1,
5103
-			], false, []
5104
-		];
5105
-
5106
-		$result[] = [
5107
-			[
5108
-				'id' => '42',
5109
-				'share_type' => IShare::TYPE_ROOM,
5110
-				'uid_owner' => 'initiator',
5111
-				'displayname_owner' => 'initiator',
5112
-				'permissions' => 1,
5113
-				'stime' => 946684862,
5114
-				'parent' => null,
5115
-				'expiration' => null,
5116
-				'token' => null,
5117
-				'uid_file_owner' => 'owner',
5118
-				'displayname_file_owner' => 'owner',
5119
-				'note' => 'personal note',
5120
-				'path' => 'file',
5121
-				'item_type' => 'file',
5122
-				'storage_id' => 'storageId',
5123
-				'storage' => 100,
5124
-				'item_source' => 3,
5125
-				'file_source' => 3,
5126
-				'file_parent' => 1,
5127
-				'file_target' => 'myTarget',
5128
-				'share_with' => 'recipientRoom',
5129
-				'share_with_displayname' => 'recipientRoomName',
5130
-				'mail_send' => 0,
5131
-				'mimetype' => 'myMimeType',
5132
-				'has_preview' => false,
5133
-				'hide_download' => 0,
5134
-				'label' => '',
5135
-				'can_edit' => false,
5136
-				'can_delete' => false,
5137
-				'item_size' => 123456,
5138
-				'item_mtime' => 1234567890,
5139
-				'is-mount-root' => false,
5140
-				'mount-type' => '',
5141
-				'attributes' => null,
5142
-				'item_permissions' => 9,
5143
-			], true, [
5144
-				'share_with_displayname' => 'recipientRoomName'
5145
-			]
5146
-		];
5147
-
5148
-		return $result;
5149
-	}
5150
-
5151
-	/**
5152
-	 *
5153
-	 * @param array $expects
5154
-	 * @param IShare $share
5155
-	 * @param bool $helperAvailable
5156
-	 * @param array $formatShareByHelper
5157
-	 */
5158
-	#[DataProvider('dataFormatRoomShare')]
5159
-	public function testFormatRoomShare(array $expects, bool $helperAvailable, array $formatShareByHelper): void {
5160
-		$file = $this->createMock(File::class);
5161
-
5162
-		$file->method('getMimeType')->willReturn('myMimeType');
5163
-		$file->method('getPath')->willReturn('file');
5164
-		$file->method('getId')->willReturn(3);
5165
-
5166
-		$parent = $this->createMock(Folder::class);
5167
-		$parent->method('getId')->willReturn(1);
5168
-		$file->method('getParent')->willReturn($parent);
5169
-
5170
-		$file->method('getSize')->willReturn(123456);
5171
-		$file->method('getMTime')->willReturn(1234567890);
5172
-
5173
-		$mountPoint = $this->createMock(IMountPoint::class);
5174
-		$mountPoint->method('getMountType')->willReturn('');
5175
-		$file->method('getMountPoint')->willReturn($mountPoint);
5176
-
5177
-		$cache = $this->createMock(ICache::class);
5178
-		$cache->method('getNumericStorageId')->willReturn(100);
5179
-		$storage = $this->createMock(IStorage::class);
5180
-		$storage->method('getId')->willReturn('storageId');
5181
-		$storage->method('getCache')->willReturn($cache);
5182
-
5183
-		$file->method('getStorage')->willReturn($storage);
5184
-
5185
-		$share = Server::get(IManager::class)->newShare();
5186
-		$share->setShareType(IShare::TYPE_ROOM)
5187
-			->setSharedWith('recipientRoom')
5188
-			->setSharedBy('initiator')
5189
-			->setShareOwner('owner')
5190
-			->setPermissions(Constants::PERMISSION_READ)
5191
-			->setNode($file)
5192
-			->setShareTime(new \DateTime('2000-01-01T00:01:02'))
5193
-			->setTarget('myTarget')
5194
-			->setNote('personal note')
5195
-			->setId(42);
5196
-
5197
-		$this->rootFolder->method('getUserFolder')
5198
-			->with($this->currentUser)
5199
-			->willReturnSelf();
5200
-
5201
-		$this->rootFolder->method('getFirstNodeById')
5202
-			->with($share->getNodeId())
5203
-			->willReturn($share->getNode());
5204
-
5205
-		$this->rootFolder->method('getRelativePath')
5206
-			->with($share->getNode()->getPath())
5207
-			->willReturnArgument(0);
5208
-
5209
-		if (!$helperAvailable) {
5210
-			$this->appManager->method('isEnabledForUser')
5211
-				->with('spreed')
5212
-				->willReturn(false);
5213
-		} else {
5214
-			$this->appManager->method('isEnabledForUser')
5215
-				->with('spreed')
5216
-				->willReturn(true);
5217
-
5218
-			// This is not possible anymore with PHPUnit 10+
5219
-			// as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
5220
-			// $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
5221
-			$helper = $this->getMockBuilder(\stdClass::class)
5222
-				->addMethods(['formatShare', 'canAccessShare'])
5223
-				->getMock();
5224
-			$helper->method('formatShare')
5225
-				->with($share)
5226
-				->willReturn($formatShareByHelper);
5227
-			$helper->method('canAccessShare')
5228
-				->with($share)
5229
-				->willReturn(true);
5230
-
5231
-			$this->serverContainer->method('get')
5232
-				->with('\OCA\Talk\Share\Helper\ShareAPIController')
5233
-				->willReturn($helper);
5234
-		}
5235
-
5236
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5237
-		$this->assertEquals($expects, $result);
5238
-	}
5239
-
5240
-	/**
5241
-	 * @return list{Folder, Folder}
5242
-	 */
5243
-	private function getNonSharedUserFolder(): array {
5244
-		$node = $this->getMockBuilder(Folder::class)->getMock();
5245
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
5246
-		$storage = $this->createMock(IStorage::class);
5247
-		$storage->method('instanceOfStorage')
5248
-			->willReturnMap([
5249
-				['OCA\Files_Sharing\External\Storage', false],
5250
-				['OCA\Files_Sharing\SharedStorage', false],
5251
-			]);
5252
-		$userFolder->method('getStorage')->willReturn($storage);
5253
-		$node->method('getStorage')->willReturn($storage);
5254
-		$node->method('getId')->willReturn(42);
5255
-		$user = $this->createMock(IUser::class);
5256
-		$user->method('getUID')->willReturn($this->currentUser);
5257
-		$node->method('getOwner')->willReturn($user);
5258
-		return [$userFolder, $node];
5259
-	}
5260
-
5261
-	/**
5262
-	 * @return list{Folder, File}
5263
-	 */
5264
-	private function getNonSharedUserFile(): array {
5265
-		$node = $this->getMockBuilder(File::class)->getMock();
5266
-		$userFolder = $this->getMockBuilder(Folder::class)->getMock();
5267
-		$storage = $this->createMock(IStorage::class);
5268
-		$storage->method('instanceOfStorage')
5269
-			->willReturnMap([
5270
-				['OCA\Files_Sharing\External\Storage', false],
5271
-				['OCA\Files_Sharing\SharedStorage', false],
5272
-			]);
5273
-		$userFolder->method('getStorage')->willReturn($storage);
5274
-		$node->method('getStorage')->willReturn($storage);
5275
-		$node->method('getId')->willReturn(42);
5276
-		return [$userFolder, $node];
5277
-	}
5278
-
5279
-	public function testPopulateTags(): void {
5280
-		$tagger = $this->createMock(ITags::class);
5281
-		$this->tagManager->method('load')
5282
-			->with('files')
5283
-			->willReturn($tagger);
5284
-		$data = [
5285
-			['file_source' => 10],
5286
-			['file_source' => 22, 'foo' => 'bar'],
5287
-			['file_source' => 42, 'x' => 'y'],
5288
-		];
5289
-		$tags = [
5290
-			10 => ['tag3'],
5291
-			42 => ['tag1', 'tag2'],
5292
-		];
5293
-		$tagger->method('getTagsForObjects')
5294
-			->with([10, 22, 42])
5295
-			->willReturn($tags);
5296
-
5297
-		$result = self::invokePrivate($this->ocs, 'populateTags', [$data]);
5298
-		$this->assertSame([
5299
-			['file_source' => 10, 'tags' => ['tag3']],
5300
-			['file_source' => 22, 'foo' => 'bar', 'tags' => []],
5301
-			['file_source' => 42, 'x' => 'y', 'tags' => ['tag1', 'tag2']],
5302
-		], $result);
5303
-	}
5304
-
5305
-	public static function trustedServerProvider(): array {
5306
-		return [
5307
-			'Trusted server' => [true, true],
5308
-			'Untrusted server' => [false, false],
5309
-		];
5310
-	}
5311
-
5312
-	#[DataProvider('trustedServerProvider')]
5313
-	public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isTrusted): void {
5314
-		$nodeId = 12;
5315
-		$nodePath = '/test.txt';
5316
-
5317
-		$node = $this->createMock(File::class);
5318
-		$node->method('getId')->willReturn($nodeId);
5319
-		$node->method('getPath')->willReturn($nodePath);
5320
-		$node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5321
-		$mountPoint = $this->createMock(IMountPoint::class);
5322
-		$mountPoint->method('getMountType')->willReturn('local');
5323
-		$node->method('getMountPoint')->willReturn($mountPoint);
5324
-		$node->method('getMimetype')->willReturn('text/plain');
5325
-		$storage = $this->createMock(IStorage::class);
5326
-		$storageCache = $this->createMock(ICache::class);
5327
-		$storageCache->method('getNumericStorageId')->willReturn(1);
5328
-		$storage->method('getCache')->willReturn($storageCache);
5329
-		$storage->method('getId')->willReturn('home::shareOwner');
5330
-		$node->method('getStorage')->willReturn($storage);
5331
-		$parent = $this->createMock(Folder::class);
5332
-		$parent->method('getId')->willReturn(2);
5333
-		$node->method('getParent')->willReturn($parent);
5334
-		$node->method('getSize')->willReturn(1234);
5335
-		$node->method('getMTime')->willReturn(1234567890);
5336
-
5337
-		$share = $this->createShare(
5338
-			1,
5339
-			IShare::TYPE_REMOTE,
5340
-			'[email protected]', // shared with
5341
-			'[email protected]',      // shared by
5342
-			'shareOwner',                 // share owner
5343
-			$node,
5344
-			Constants::PERMISSION_READ,
5345
-			time(),
5346
-			null,
5347
-			2,
5348
-			$nodePath,
5349
-			$nodeId
5350
-		);
5351
-
5352
-		$this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5353
-
5354
-		$this->rootFolder->method('getUserFolder')
5355
-			->with($this->currentUser)
5356
-			->willReturnSelf();
5357
-
5358
-		$this->rootFolder->method('getFirstNodeById')
5359
-			->with($share->getNodeId())
5360
-			->willReturn($node);
5361
-
5362
-		$this->rootFolder->method('getRelativePath')
5363
-			->with($node->getPath())
5364
-			->willReturnArgument(0);
5365
-
5366
-		$serverName = 'remoteserver.com';
5367
-		$this->trustedServers->method('isTrustedServer')
5368
-			->with($serverName)
5369
-			->willReturn($isKnownServer);
5370
-
5371
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5372
-
5373
-		$this->assertSame($isTrusted, $result['is_trusted_server']);
5374
-	}
5375
-
5376
-	public function testFormatShareWithFederatedShareWithAtInUsername(): void {
5377
-		$nodeId = 12;
5378
-		$nodePath = '/test.txt';
5379
-
5380
-		$node = $this->createMock(File::class);
5381
-		$node->method('getId')->willReturn($nodeId);
5382
-		$node->method('getPath')->willReturn($nodePath);
5383
-		$node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5384
-		$mountPoint = $this->createMock(IMountPoint::class);
5385
-		$mountPoint->method('getMountType')->willReturn('local');
5386
-		$node->method('getMountPoint')->willReturn($mountPoint);
5387
-		$node->method('getMimetype')->willReturn('text/plain');
5388
-		$storage = $this->createMock(IStorage::class);
5389
-		$storageCache = $this->createMock(ICache::class);
5390
-		$storageCache->method('getNumericStorageId')->willReturn(1);
5391
-		$storage->method('getCache')->willReturn($storageCache);
5392
-		$storage->method('getId')->willReturn('home::shareOwner');
5393
-		$node->method('getStorage')->willReturn($storage);
5394
-		$parent = $this->createMock(Folder::class);
5395
-		$parent->method('getId')->willReturn(2);
5396
-		$node->method('getParent')->willReturn($parent);
5397
-		$node->method('getSize')->willReturn(1234);
5398
-		$node->method('getMTime')->willReturn(1234567890);
5399
-
5400
-		$share = $this->createShare(
5401
-			1,
5402
-			IShare::TYPE_REMOTE,
5403
-			'[email protected]@remoteserver.com',
5404
-			'[email protected]',
5405
-			'shareOwner',
5406
-			$node,
5407
-			Constants::PERMISSION_READ,
5408
-			time(),
5409
-			null,
5410
-			2,
5411
-			$nodePath,
5412
-			$nodeId
5413
-		);
5414
-
5415
-		$this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5416
-
5417
-		$this->rootFolder->method('getUserFolder')
5418
-			->with($this->currentUser)
5419
-			->willReturnSelf();
5420
-
5421
-		$this->rootFolder->method('getFirstNodeById')
5422
-			->with($share->getNodeId())
5423
-			->willReturn($node);
5424
-
5425
-		$this->rootFolder->method('getRelativePath')
5426
-			->with($node->getPath())
5427
-			->willReturnArgument(0);
5428
-
5429
-		$serverName = 'remoteserver.com';
5430
-		$this->trustedServers->method('isTrustedServer')
5431
-			->with($serverName)
5432
-			->willReturn(true);
5433
-
5434
-		$result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5435
-
5436
-		$this->assertTrue($result['is_trusted_server']);
5437
-	}
5438
-
5439
-	public function testOwnerCanAlwaysDownload(): void {
5440
-		$ocs = $this->mockFormatShare();
5441
-
5442
-		$share = $this->createMock(IShare::class);
5443
-		$node = $this->createMock(File::class);
5444
-		$userFolder = $this->createMock(Folder::class);
5445
-		$owner = $this->createMock(IUser::class);
5446
-
5447
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5448
-		$share->method('getNodeId')->willReturn(42);
5449
-		$node->method('getOwner')->willReturn($owner);
5450
-		$owner->method('getUID')->willReturn('sharedByUser');
5451
-
5452
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5453
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5454
-
5455
-		// Expect hideDownload to be set to false since owner can always download
5456
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5457
-
5458
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5459
-	}
5460
-
5461
-	public function testParentHideDownloadEnforcedOnChild(): void {
5462
-		$ocs = $this->mockFormatShare();
5463
-
5464
-		$share = $this->createMock(IShare::class);
5465
-		$node = $this->createMock(File::class);
5466
-		$userFolder = $this->createMock(Folder::class);
5467
-		$owner = $this->createMock(IUser::class);
5468
-		$storage = $this->createMock(SharedStorage::class);
5469
-		$originalShare = $this->createMock(IShare::class);
5470
-
5471
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5472
-		$share->method('getNodeId')->willReturn(42);
5473
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5474
-		$node->method('getOwner')->willReturn($owner);
5475
-		$owner->method('getUID')->willReturn('differentOwner');
5476
-		$node->method('getStorage')->willReturn($storage);
5477
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5478
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5479
-		$storage->method('getShare')->willReturn($originalShare);
5480
-		$originalShare->method('getHideDownload')->willReturn(true); // Parent hides download
5481
-		$originalShare->method('getAttributes')->willReturn(null);
5482
-
5483
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5484
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5485
-
5486
-		// Should be forced to hide download due to parent restriction
5487
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5488
-
5489
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5490
-	}
5491
-
5492
-	public function testUserCanHideWhenParentAllows(): void {
5493
-		$ocs = $this->mockFormatShare();
5494
-
5495
-		$share = $this->createMock(IShare::class);
5496
-		$node = $this->createMock(File::class);
5497
-		$userFolder = $this->createMock(Folder::class);
5498
-		$owner = $this->createMock(IUser::class);
5499
-		$storage = $this->createMock(SharedStorage::class);
5500
-		$originalShare = $this->createMock(IShare::class);
5501
-
5502
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5503
-		$share->method('getNodeId')->willReturn(42);
5504
-		$share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5505
-		$node->method('getOwner')->willReturn($owner);
5506
-		$owner->method('getUID')->willReturn('differentOwner');
5507
-		$node->method('getStorage')->willReturn($storage);
5508
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5509
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5510
-		$storage->method('getShare')->willReturn($originalShare);
5511
-		$originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5512
-		$originalShare->method('getAttributes')->willReturn(null);
5513
-
5514
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5515
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5516
-
5517
-		// Should respect user's choice to hide downloads
5518
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5519
-
5520
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5521
-	}
5522
-
5523
-	public function testParentDownloadAttributeInherited(): void {
5524
-		$ocs = $this->mockFormatShare();
5525
-
5526
-		$share = $this->createMock(IShare::class);
5527
-		$node = $this->createMock(File::class);
5528
-		$userFolder = $this->createMock(Folder::class);
5529
-		$owner = $this->createMock(IUser::class);
5530
-		$storage = $this->createMock(SharedStorage::class);
5531
-		$originalShare = $this->createMock(IShare::class);
5532
-		$attributes = $this->createMock(\OCP\Share\IAttributes::class);
5533
-		$shareAttributes = $this->createMock(\OCP\Share\IAttributes::class);
5534
-
5535
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5536
-		$share->method('getNodeId')->willReturn(42);
5537
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5538
-		$share->method('getAttributes')->willReturn($shareAttributes);
5539
-		$share->method('newAttributes')->willReturn($shareAttributes);
5540
-		$node->method('getOwner')->willReturn($owner);
5541
-		$owner->method('getUID')->willReturn('differentOwner');
5542
-		$node->method('getStorage')->willReturn($storage);
5543
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5544
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5545
-		$storage->method('getShare')->willReturn($originalShare);
5546
-		$originalShare->method('getHideDownload')->willReturn(false);
5547
-		$originalShare->method('getAttributes')->willReturn($attributes);
5548
-		$attributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); // Parent forbids download
5549
-
5550
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5551
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5552
-
5553
-		// Should be forced to hide download and set download attribute to false
5554
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5555
-		$shareAttributes->expects($this->once())->method('setAttribute')->with('permissions', 'download', false);
5556
-		$share->expects($this->once())->method('setAttributes')->with($shareAttributes);
5557
-
5558
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5559
-	}
5560
-
5561
-	public function testFederatedStorageRespectsUserChoice(): void {
5562
-		$ocs = $this->mockFormatShare();
5563
-
5564
-		$share = $this->createMock(IShare::class);
5565
-		$node = $this->createMock(File::class);
5566
-		$userFolder = $this->createMock(Folder::class);
5567
-		$owner = $this->createMock(IUser::class);
5568
-		$storage = $this->createMock(\OCA\Files_Sharing\External\Storage::class);
5569
-
5570
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5571
-		$share->method('getNodeId')->willReturn(42);
5572
-		$share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5573
-		$node->method('getOwner')->willReturn($owner);
5574
-		$owner->method('getUID')->willReturn('differentOwner');
5575
-		$node->method('getStorage')->willReturn($storage);
5576
-		$storage->method('instanceOfStorage')->willReturnMap([
5577
-			[SharedStorage::class, false],
5578
-			[\OCA\Files_Sharing\External\Storage::class, true]
5579
-		]);
5580
-
5581
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5582
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5583
-
5584
-		// For federated storage, should respect user's choice
5585
-		$share->expects($this->once())->method('setHideDownload')->with(true);
5586
-
5587
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5588
-	}
5589
-
5590
-	public function testUserAllowsDownloadWhenParentPermits(): void {
5591
-		$ocs = $this->mockFormatShare();
5592
-
5593
-		$share = $this->createMock(IShare::class);
5594
-		$node = $this->createMock(File::class);
5595
-		$userFolder = $this->createMock(Folder::class);
5596
-		$owner = $this->createMock(IUser::class);
5597
-		$storage = $this->createMock(SharedStorage::class);
5598
-		$originalShare = $this->createMock(IShare::class);
5599
-
5600
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5601
-		$share->method('getNodeId')->willReturn(42);
5602
-		$share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5603
-		$node->method('getOwner')->willReturn($owner);
5604
-		$owner->method('getUID')->willReturn('differentOwner');
5605
-		$node->method('getStorage')->willReturn($storage);
5606
-		$storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5607
-		$storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5608
-		$storage->method('getShare')->willReturn($originalShare);
5609
-		$originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5610
-		$originalShare->method('getAttributes')->willReturn(null);
5611
-
5612
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5613
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5614
-
5615
-		// Should allow downloads as both user and parent permit it
5616
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5617
-
5618
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5619
-	}
5620
-
5621
-	public function testWrapperStorageUnwrapped(): void {
5622
-		$ocs = $this->mockFormatShare();
5623
-
5624
-		$share = $this->createMock(IShare::class);
5625
-		$node = $this->createMock(File::class);
5626
-		$userFolder = $this->createMock(Folder::class);
5627
-		$owner = $this->createMock(IUser::class);
5628
-		$wrapperStorage = $this->createMock(Wrapper::class);
5629
-		$innerStorage = $this->createMock(SharedStorage::class);
5630
-		$originalShare = $this->createMock(IShare::class);
5631
-
5632
-		$share->method('getSharedBy')->willReturn('sharedByUser');
5633
-		$share->method('getNodeId')->willReturn(42);
5634
-		$share->method('getHideDownload')->willReturn(false);
5635
-		$node->method('getOwner')->willReturn($owner);
5636
-		$owner->method('getUID')->willReturn('differentOwner');
5637
-		$node->method('getStorage')->willReturn($wrapperStorage);
5638
-		$wrapperStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5639
-		$wrapperStorage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($innerStorage);
5640
-		$innerStorage->method('getShare')->willReturn($originalShare);
5641
-		$originalShare->method('getHideDownload')->willReturn(false);
5642
-		$originalShare->method('getAttributes')->willReturn(null);
5643
-
5644
-		$userFolder->method('getById')->with(42)->willReturn([$node]);
5645
-		$this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5646
-
5647
-		$share->expects($this->once())->method('setHideDownload')->with(false);
5648
-
5649
-		$this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5650
-	}
594
+    public function createShare(
595
+        int $id,
596
+        int $shareType,
597
+        ?string $sharedWith,
598
+        string $sharedBy,
599
+        string $shareOwner,
600
+        File|Folder|null $node,
601
+        int $permissions,
602
+        int $shareTime,
603
+        ?\DateTime $expiration,
604
+        int $parent,
605
+        string $target,
606
+        int $mail_send,
607
+        string $note = '',
608
+        ?string $token = null,
609
+        ?string $password = null,
610
+        string $label = '',
611
+        ?IShareAttributes $attributes = null,
612
+    ): MockObject {
613
+        $share = $this->createMock(IShare::class);
614
+        $share->method('getId')->willReturn($id);
615
+        $share->method('getShareType')->willReturn($shareType);
616
+        $share->method('getSharedWith')->willReturn($sharedWith);
617
+        $share->method('getSharedBy')->willReturn($sharedBy);
618
+        $share->method('getShareOwner')->willReturn($shareOwner);
619
+        $share->method('getNode')->willReturn($node);
620
+        $share->method('getPermissions')->willReturn($permissions);
621
+        $share->method('getNote')->willReturn($note);
622
+        $share->method('getLabel')->willReturn($label);
623
+        $share->method('getAttributes')->willReturn($attributes);
624
+        $time = new \DateTime();
625
+        $time->setTimestamp($shareTime);
626
+        $share->method('getShareTime')->willReturn($time);
627
+        $share->method('getExpirationDate')->willReturn($expiration);
628
+        $share->method('getTarget')->willReturn($target);
629
+        $share->method('getMailSend')->willReturn($mail_send);
630
+        $share->method('getToken')->willReturn($token);
631
+        $share->method('getPassword')->willReturn($password);
632
+
633
+        if ($shareType === IShare::TYPE_USER
634
+            || $shareType === IShare::TYPE_GROUP
635
+            || $shareType === IShare::TYPE_LINK) {
636
+            $share->method('getFullId')->willReturn('ocinternal:' . $id);
637
+        }
638
+
639
+        return $share;
640
+    }
641
+
642
+    public static function dataGetShare(): array {
643
+        $data = [];
644
+
645
+        $file = [
646
+            'class' => File::class,
647
+            'id' => 1,
648
+            'path' => 'file',
649
+            'mimeType' => 'myMimeType',
650
+        ];
651
+
652
+        $folder = [
653
+            'class' => Folder::class,
654
+            'id' => 2,
655
+            'path' => 'folder',
656
+            'mimeType' => 'myFolderMimeType',
657
+        ];
658
+
659
+        // File shared with user
660
+        $share = [
661
+            100,
662
+            IShare::TYPE_USER,
663
+            'userId',
664
+            'initiatorId',
665
+            'ownerId',
666
+            $file,
667
+            4,
668
+            5,
669
+            null,
670
+            6,
671
+            'target',
672
+            0,
673
+            'personal note',
674
+            null,
675
+            null,
676
+            '',
677
+            [],
678
+        ];
679
+        $expected = [
680
+            'id' => 100,
681
+            'share_type' => IShare::TYPE_USER,
682
+            'share_with' => 'userId',
683
+            'share_with_displayname' => 'userDisplay',
684
+            'share_with_displayname_unique' => '[email protected]',
685
+            'uid_owner' => 'initiatorId',
686
+            'displayname_owner' => 'initiatorDisplay',
687
+            'item_type' => 'file',
688
+            'item_source' => 1,
689
+            'file_source' => 1,
690
+            'file_target' => 'target',
691
+            'file_parent' => 3,
692
+            'token' => null,
693
+            'expiration' => null,
694
+            'permissions' => 4,
695
+            'stime' => 5,
696
+            'parent' => null,
697
+            'storage_id' => 'STORAGE',
698
+            'path' => 'file',
699
+            'storage' => 101,
700
+            'mail_send' => 0,
701
+            'uid_file_owner' => 'ownerId',
702
+            'note' => 'personal note',
703
+            'label' => '',
704
+            'displayname_file_owner' => 'ownerDisplay',
705
+            'mimetype' => 'myMimeType',
706
+            'has_preview' => false,
707
+            'hide_download' => 0,
708
+            'can_edit' => false,
709
+            'can_delete' => false,
710
+            'item_size' => 123465,
711
+            'item_mtime' => 1234567890,
712
+            'item_permissions' => 4,
713
+            'is-mount-root' => false,
714
+            'mount-type' => '',
715
+        ];
716
+        $data['File shared with user'] = [$share, $expected, true];
717
+
718
+        // Folder shared with group
719
+        $share = [
720
+            101,
721
+            IShare::TYPE_GROUP,
722
+            'groupId',
723
+            'initiatorId',
724
+            'ownerId',
725
+            $folder,
726
+            4,
727
+            5,
728
+            null,
729
+            6,
730
+            'target',
731
+            0,
732
+            'personal note',
733
+            null,
734
+            null,
735
+            '',
736
+            [],
737
+        ];
738
+        $expected = [
739
+            'id' => 101,
740
+            'share_type' => IShare::TYPE_GROUP,
741
+            'share_with' => 'groupId',
742
+            'share_with_displayname' => 'groupId',
743
+            'uid_owner' => 'initiatorId',
744
+            'displayname_owner' => 'initiatorDisplay',
745
+            'item_type' => 'folder',
746
+            'item_source' => 2,
747
+            'file_source' => 2,
748
+            'file_target' => 'target',
749
+            'file_parent' => 3,
750
+            'token' => null,
751
+            'expiration' => null,
752
+            'permissions' => 4,
753
+            'stime' => 5,
754
+            'parent' => null,
755
+            'storage_id' => 'STORAGE',
756
+            'path' => 'folder',
757
+            'storage' => 101,
758
+            'mail_send' => 0,
759
+            'uid_file_owner' => 'ownerId',
760
+            'note' => 'personal note',
761
+            'label' => '',
762
+            'displayname_file_owner' => 'ownerDisplay',
763
+            'mimetype' => 'myFolderMimeType',
764
+            'has_preview' => false,
765
+            'hide_download' => 0,
766
+            'can_edit' => false,
767
+            'can_delete' => false,
768
+            'item_size' => 123465,
769
+            'item_mtime' => 1234567890,
770
+            'item_permissions' => 4,
771
+            'is-mount-root' => false,
772
+            'mount-type' => '',
773
+        ];
774
+        $data['Folder shared with group'] = [$share, $expected, true];
775
+
776
+        // File shared by link with Expire
777
+        $expire = \DateTime::createFromFormat('Y-m-d h:i:s', '2000-01-02 01:02:03');
778
+        $share = [
779
+            101,
780
+            IShare::TYPE_LINK,
781
+            null,
782
+            'initiatorId',
783
+            'ownerId',
784
+            $folder,
785
+            4,
786
+            5,
787
+            $expire,
788
+            6,
789
+            'target',
790
+            0,
791
+            'personal note',
792
+            'token',
793
+            'password',
794
+            'first link share'
795
+        ];
796
+        $expected = [
797
+            'id' => 101,
798
+            'share_type' => IShare::TYPE_LINK,
799
+            'password' => 'password',
800
+            'share_with' => 'password',
801
+            'share_with_displayname' => '(Shared link)',
802
+            'send_password_by_talk' => false,
803
+            'uid_owner' => 'initiatorId',
804
+            'displayname_owner' => 'initiatorDisplay',
805
+            'item_type' => 'folder',
806
+            'item_source' => 2,
807
+            'file_source' => 2,
808
+            'file_target' => 'target',
809
+            'file_parent' => 3,
810
+            'token' => 'token',
811
+            'expiration' => '2000-01-02 00:00:00',
812
+            'permissions' => 4,
813
+            'attributes' => null,
814
+            'stime' => 5,
815
+            'parent' => null,
816
+            'storage_id' => 'STORAGE',
817
+            'path' => 'folder',
818
+            'storage' => 101,
819
+            'mail_send' => 0,
820
+            'url' => 'url',
821
+            'uid_file_owner' => 'ownerId',
822
+            'note' => 'personal note',
823
+            'label' => 'first link share',
824
+            'displayname_file_owner' => 'ownerDisplay',
825
+            'mimetype' => 'myFolderMimeType',
826
+            'has_preview' => false,
827
+            'hide_download' => 0,
828
+            'can_edit' => false,
829
+            'can_delete' => false,
830
+            'item_size' => 123465,
831
+            'item_mtime' => 1234567890,
832
+            'item_permissions' => 4,
833
+            'is-mount-root' => false,
834
+            'mount-type' => '',
835
+        ];
836
+        $data['File shared by link with Expire'] = [$share, $expected, false];
837
+
838
+        return $data;
839
+    }
840
+
841
+    #[DataProvider('dataGetShare')]
842
+    public function testGetShare(array $shareParams, array $result, bool $attributes): void {
843
+
844
+        $cache = $this->createMock(ICache::class);
845
+        $cache->method('getNumericStorageId')->willReturn(101);
846
+
847
+        $storage = $this->createMock(IStorage::class);
848
+        $storage->method('getId')->willReturn('STORAGE');
849
+        $storage->method('getCache')->willReturn($cache);
850
+
851
+        $parentFolder = $this->createMock(Folder::class);
852
+        $parentFolder->method('getId')->willReturn(3);
853
+
854
+        $mountPoint = $this->createMock(IMountPoint::class);
855
+        $mountPoint->method('getMountType')->willReturn('');
856
+
857
+        $nodeParams = $shareParams[5];
858
+        $node = $this->createMock($nodeParams['class']);
859
+        $node->method('getId')->willReturn($nodeParams['id']);
860
+        $node->method('getPath')->willReturn($nodeParams['path']);
861
+        $node->method('getStorage')->willReturn($storage);
862
+        $node->method('getParent')->willReturn($parentFolder);
863
+        $node->method('getSize')->willReturn(123465);
864
+        $node->method('getMTime')->willReturn(1234567890);
865
+        $node->method('getMimeType')->willReturn($nodeParams['mimeType']);
866
+        $node->method('getMountPoint')->willReturn($mountPoint);
867
+
868
+        $shareParams[5] = $node;
869
+
870
+        if ($attributes) {
871
+            [$shareAttributes, $shareAttributesReturnJson] = $this->mockShareAttributes();
872
+            $result['attributes'] = $shareAttributesReturnJson;
873
+            $shareParams[16] = $shareAttributes;
874
+        }
875
+
876
+        $share = $this->createShare(...$shareParams);
877
+        /** @var ShareAPIController&MockObject $ocs */
878
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
879
+            ->setConstructorArgs([
880
+                $this->appName,
881
+                $this->request,
882
+                $this->shareManager,
883
+                $this->groupManager,
884
+                $this->userManager,
885
+                $this->rootFolder,
886
+                $this->urlGenerator,
887
+                $this->l,
888
+                $this->config,
889
+                $this->appConfig,
890
+                $this->appManager,
891
+                $this->serverContainer,
892
+                $this->userStatusManager,
893
+                $this->previewManager,
894
+                $this->dateTimeZone,
895
+                $this->logger,
896
+                $this->factory,
897
+                $this->mailer,
898
+                $this->tagManager,
899
+                $this->getEmailValidatorWithStrictEmailCheck(),
900
+                $this->trustedServers,
901
+                $this->currentUser,
902
+            ])
903
+            ->onlyMethods(['canAccessShare'])
904
+            ->getMock();
905
+
906
+        $ocs->expects($this->any())
907
+            ->method('canAccessShare')
908
+            ->willReturn(true);
909
+
910
+        $this->shareManager
911
+            ->expects($this->any())
912
+            ->method('getShareById')
913
+            ->with($share->getFullId(), 'currentUser')
914
+            ->willReturn($share);
915
+
916
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
917
+        $userFolder
918
+            ->method('getRelativePath')
919
+            ->willReturnArgument(0);
920
+
921
+        $userFolder->method('getById')
922
+            ->with($share->getNodeId())
923
+            ->willReturn([$share->getNode()]);
924
+        $userFolder->method('getFirstNodeById')
925
+            ->with($share->getNodeId())
926
+            ->willReturn($share->getNode());
927
+
928
+        $this->rootFolder->method('getUserFolder')
929
+            ->with($this->currentUser)
930
+            ->willReturn($userFolder);
931
+
932
+        $this->urlGenerator
933
+            ->method('linkToRouteAbsolute')
934
+            ->willReturn('url');
935
+
936
+        $initiator = $this->getMockBuilder(IUser::class)->getMock();
937
+        $initiator->method('getUID')->willReturn('initiatorId');
938
+        $initiator->method('getDisplayName')->willReturn('initiatorDisplay');
939
+
940
+        $owner = $this->getMockBuilder(IUser::class)->getMock();
941
+        $owner->method('getUID')->willReturn('ownerId');
942
+        $owner->method('getDisplayName')->willReturn('ownerDisplay');
943
+
944
+        $user = $this->getMockBuilder(IUser::class)->getMock();
945
+        $user->method('getUID')->willReturn('userId');
946
+        $user->method('getDisplayName')->willReturn('userDisplay');
947
+        $user->method('getSystemEMailAddress')->willReturn('[email protected]');
948
+
949
+        $group = $this->getMockBuilder(IGroup::class)->getMock();
950
+        $group->method('getGID')->willReturn('groupId');
951
+
952
+        $this->userManager->method('get')->willReturnMap([
953
+            ['userId', $user],
954
+            ['initiatorId', $initiator],
955
+            ['ownerId', $owner],
956
+        ]);
957
+        $this->groupManager->method('get')->willReturnMap([
958
+            ['group', $group],
959
+        ]);
960
+        $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
961
+
962
+        $data = $ocs->getShare((string)$share->getId())->getData()[0];
963
+        $this->assertEquals($result, $data);
964
+    }
965
+
966
+
967
+    public function testGetShareInvalidNode(): void {
968
+        $this->expectException(OCSNotFoundException::class);
969
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
970
+
971
+        $share = Server::get(IManager::class)->newShare();
972
+        $share->setSharedBy('initiator')
973
+            ->setSharedWith('recipient')
974
+            ->setShareOwner('owner');
975
+
976
+        $this->shareManager
977
+            ->expects($this->once())
978
+            ->method('getShareById')
979
+            ->with('ocinternal:42', 'currentUser')
980
+            ->willReturn($share);
981
+
982
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
983
+        $this->rootFolder->method('getUserFolder')
984
+            ->with($this->currentUser)
985
+            ->willReturn($userFolder);
986
+
987
+        $this->ocs->getShare('42');
988
+    }
989
+
990
+    public static function dataGetShares(): array {
991
+        $file1 = [
992
+            'class' => File::class,
993
+            'methods' => [
994
+                'getName' => 'file1',
995
+            ]
996
+        ];
997
+        $file2 = [
998
+            'class' => File::class,
999
+            'methods' => [
1000
+                'getName' => 'file2',
1001
+            ]
1002
+        ];
1003
+
1004
+        $folder = [
1005
+            'class' => Folder::class,
1006
+            'methods' => [
1007
+                'getDirectoryListing' => [$file1, $file2]
1008
+            ]
1009
+        ];
1010
+
1011
+        $file1UserShareOwner = [
1012
+            'type' => IShare::TYPE_USER,
1013
+            'sharedWith' => 'recipient',
1014
+            'sharedBy' => 'initiator',
1015
+            'owner' => 'currentUser',
1016
+            'node' => $file1,
1017
+            'id' => 4,
1018
+        ];
1019
+
1020
+        $file1UserShareOwnerExpected = [
1021
+            'id' => 4,
1022
+            'share_type' => IShare::TYPE_USER,
1023
+        ];
1024
+
1025
+        $file1UserShareInitiator = [
1026
+            'type' => IShare::TYPE_USER,
1027
+            'sharedWith' => 'recipient',
1028
+            'sharedBy' => 'currentUser',
1029
+            'owner' => 'owner',
1030
+            'node' => $file1,
1031
+            'id' => 8,
1032
+        ];
1033
+
1034
+        $file1UserShareInitiatorExpected = [
1035
+            'id' => 8,
1036
+            'share_type' => IShare::TYPE_USER,
1037
+        ];
1038
+
1039
+        $file1UserShareRecipient = [
1040
+            'type' => IShare::TYPE_USER,
1041
+            'sharedWith' => 'currentUser',
1042
+            'sharedBy' => 'initiator',
1043
+            'owner' => 'owner',
1044
+            'node' => $file1,
1045
+            'id' => 15,
1046
+        ];
1047
+
1048
+        $file1UserShareRecipientExpected = [
1049
+            'id' => 15,
1050
+            'share_type' => IShare::TYPE_USER,
1051
+        ];
1052
+
1053
+        $file1UserShareOther = [
1054
+            'type' => IShare::TYPE_USER,
1055
+            'sharedWith' => 'recipient',
1056
+            'sharedBy' => 'initiator',
1057
+            'owner' => 'owner',
1058
+            'node' => $file1,
1059
+            'id' => 16,
1060
+        ];
1061
+
1062
+        $file1UserShareOtherExpected = [
1063
+            'id' => 16,
1064
+            'share_type' => IShare::TYPE_USER,
1065
+        ];
1066
+
1067
+        $file1GroupShareOwner = [
1068
+            'type' => IShare::TYPE_GROUP,
1069
+            'sharedWith' => 'recipient',
1070
+            'sharedBy' => 'initiator',
1071
+            'owner' => 'currentUser',
1072
+            'node' => $file1,
1073
+            'id' => 23,
1074
+        ];
1075
+
1076
+        $file1GroupShareOwnerExpected = [
1077
+            'id' => 23,
1078
+            'share_type' => IShare::TYPE_GROUP,
1079
+        ];
1080
+
1081
+        $file1GroupShareRecipient = [
1082
+            'type' => IShare::TYPE_GROUP,
1083
+            'sharedWith' => 'currentUserGroup',
1084
+            'sharedBy' => 'initiator',
1085
+            'owner' => 'owner',
1086
+            'node' => $file1,
1087
+            'id' => 42,
1088
+        ];
1089
+
1090
+        $file1GroupShareRecipientExpected = [
1091
+            'id' => 42,
1092
+            'share_type' => IShare::TYPE_GROUP,
1093
+        ];
1094
+
1095
+        $file1GroupShareOther = [
1096
+            'type' => IShare::TYPE_GROUP,
1097
+            'sharedWith' => 'recipient',
1098
+            'sharedBy' => 'initiator',
1099
+            'owner' => 'owner',
1100
+            'node' => $file1,
1101
+            'id' => 108,
1102
+        ];
1103
+
1104
+        $file1LinkShareOwner = [
1105
+            'type' => IShare::TYPE_LINK,
1106
+            'sharedWith' => 'recipient',
1107
+            'sharedBy' => 'initiator',
1108
+            'owner' => 'currentUser',
1109
+            'node' => $file1,
1110
+            'id' => 415,
1111
+        ];
1112
+
1113
+        $file1LinkShareOwnerExpected = [
1114
+            'id' => 415,
1115
+            'share_type' => IShare::TYPE_LINK,
1116
+        ];
1117
+
1118
+        $file1EmailShareOwner = [
1119
+            'type' => IShare::TYPE_EMAIL,
1120
+            'sharedWith' => 'recipient',
1121
+            'sharedBy' => 'initiator',
1122
+            'owner' => 'currentUser',
1123
+            'node' => $file1,
1124
+            'id' => 416,
1125
+        ];
1126
+
1127
+        $file1EmailShareOwnerExpected = [
1128
+            'id' => 416,
1129
+            'share_type' => IShare::TYPE_EMAIL,
1130
+        ];
1131
+
1132
+        $file1CircleShareOwner = [
1133
+            'type' => IShare::TYPE_CIRCLE,
1134
+            'sharedWith' => 'recipient',
1135
+            'sharedBy' => 'initiator',
1136
+            'owner' => 'currentUser',
1137
+            'node' => $file1,
1138
+            'id' => 423,
1139
+        ];
1140
+
1141
+        $file1CircleShareOwnerExpected = [
1142
+            'id' => 423,
1143
+            'share_type' => IShare::TYPE_CIRCLE,
1144
+        ];
1145
+
1146
+        $file1RoomShareOwner = [
1147
+            'type' => IShare::TYPE_ROOM,
1148
+            'sharedWith' => 'recipient',
1149
+            'sharedBy' => 'initiator',
1150
+            'owner' => 'currentUser',
1151
+            'node' => $file1,
1152
+            'id' => 442,
1153
+        ];
1154
+
1155
+        $file1RoomShareOwnerExpected = [
1156
+            'id' => 442,
1157
+            'share_type' => IShare::TYPE_ROOM,
1158
+        ];
1159
+
1160
+        $file1RemoteShareOwner = [
1161
+            'type' => IShare::TYPE_REMOTE,
1162
+            'sharedWith' => 'recipient',
1163
+            'sharedBy' => 'initiator',
1164
+            'owner' => 'currentUser',
1165
+            'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1166
+            'node' => $file1,
1167
+            'id' => 815,
1168
+        ];
1169
+
1170
+        $file1RemoteShareOwnerExpected = [
1171
+            'id' => 815,
1172
+            'share_type' => IShare::TYPE_REMOTE,
1173
+        ];
1174
+
1175
+        $file1RemoteGroupShareOwner = [
1176
+            'type' => IShare::TYPE_REMOTE_GROUP,
1177
+            'sharedWith' => 'recipient',
1178
+            'sharedBy' => 'initiator',
1179
+            'owner' => 'currentUser',
1180
+            'expirationDate' => new \DateTime('2000-01-01T01:02:03'),
1181
+            'node' => $file1,
1182
+            'id' => 816,
1183
+        ];
1184
+
1185
+        $file1RemoteGroupShareOwnerExpected = [
1186
+            'id' => 816,
1187
+            'share_type' => IShare::TYPE_REMOTE_GROUP,
1188
+        ];
1189
+
1190
+        $file2UserShareOwner = [
1191
+            'type' => IShare::TYPE_USER,
1192
+            'sharedWith' => 'recipient',
1193
+            'sharedBy' => 'initiator',
1194
+            'owner' => 'currentUser',
1195
+            'node' => $file2,
1196
+            'id' => 823,
1197
+        ];
1198
+
1199
+        $file2UserShareOwnerExpected = [
1200
+            'id' => 823,
1201
+            'share_type' => IShare::TYPE_USER,
1202
+        ];
1203
+
1204
+        $data = [
1205
+            [
1206
+                [
1207
+                    'node' => $file1,
1208
+                ],
1209
+                [
1210
+                    'file1' => [
1211
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1212
+                    ],
1213
+                ],
1214
+                [
1215
+                ],
1216
+                [
1217
+                    $file1UserShareOwnerExpected
1218
+                ]
1219
+            ],
1220
+            [
1221
+                [
1222
+                    'node' => $file1,
1223
+                ],
1224
+                [
1225
+                    'file1' => [
1226
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1227
+                    ],
1228
+                ],
1229
+                [
1230
+                ],
1231
+                [
1232
+                    $file1UserShareOwnerExpected,
1233
+                ]
1234
+            ],
1235
+            [
1236
+                [
1237
+                    'node' => $file1,
1238
+                ],
1239
+                [
1240
+                    'file1' => [
1241
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1242
+                    ],
1243
+                ],
1244
+                [
1245
+                ],
1246
+                [
1247
+                    $file1UserShareOwnerExpected,
1248
+                    $file1UserShareInitiatorExpected,
1249
+                    $file1UserShareOtherExpected,
1250
+                ]
1251
+            ],
1252
+            [
1253
+                [
1254
+                    'node' => $file1,
1255
+                ],
1256
+                [
1257
+                    'file1' => [
1258
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1259
+                    ],
1260
+                ],
1261
+                [
1262
+                ],
1263
+                [
1264
+                    $file1UserShareInitiatorExpected,
1265
+                ]
1266
+            ],
1267
+            [
1268
+                [
1269
+                    'node' => $file1,
1270
+                ],
1271
+                [
1272
+                    'file1' => [
1273
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1274
+                        IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1275
+                    ],
1276
+                ],
1277
+                [
1278
+                ],
1279
+                [
1280
+                    $file1UserShareOwnerExpected,
1281
+                    $file1GroupShareRecipientExpected,
1282
+                ]
1283
+            ],
1284
+            [
1285
+                [
1286
+                    'node' => $file1,
1287
+                ],
1288
+                [
1289
+                    'file1' => [
1290
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1291
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1292
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1293
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1294
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1295
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1296
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1297
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1298
+                    ],
1299
+                ],
1300
+                [
1301
+                ],
1302
+                [
1303
+                    $file1UserShareOwnerExpected,
1304
+                    $file1GroupShareOwnerExpected,
1305
+                    $file1LinkShareOwnerExpected,
1306
+                    $file1EmailShareOwnerExpected,
1307
+                    $file1CircleShareOwnerExpected,
1308
+                    $file1RoomShareOwnerExpected,
1309
+                ]
1310
+            ],
1311
+            [
1312
+                [
1313
+                    'node' => $file1,
1314
+                ],
1315
+                [
1316
+                    'file1' => [
1317
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1318
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1319
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1320
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1321
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1322
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1323
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1324
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1325
+                    ],
1326
+                ],
1327
+                [
1328
+                    IShare::TYPE_REMOTE => true,
1329
+                    IShare::TYPE_REMOTE_GROUP => true,
1330
+                ],
1331
+                [
1332
+                    $file1UserShareOwnerExpected,
1333
+                    $file1GroupShareOwnerExpected,
1334
+                    $file1LinkShareOwnerExpected,
1335
+                    $file1EmailShareOwnerExpected,
1336
+                    $file1CircleShareOwnerExpected,
1337
+                    $file1RoomShareOwnerExpected,
1338
+                    $file1RemoteShareOwnerExpected,
1339
+                    $file1RemoteGroupShareOwnerExpected,
1340
+                ]
1341
+            ],
1342
+            [
1343
+                [
1344
+                    'node' => $folder,
1345
+                    'subfiles' => 'true',
1346
+                ],
1347
+                [
1348
+                    'file1' => [
1349
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1350
+                    ],
1351
+                    'file2' => [
1352
+                        IShare::TYPE_USER => [$file2UserShareOwner],
1353
+                    ],
1354
+                ],
1355
+                [
1356
+                ],
1357
+                [
1358
+                    $file1UserShareOwnerExpected,
1359
+                    $file2UserShareOwnerExpected,
1360
+                ]
1361
+            ],
1362
+            [
1363
+                [
1364
+                    'node' => $folder,
1365
+                    'subfiles' => 'true',
1366
+                ],
1367
+                [
1368
+                    'file1' => [
1369
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareOwner, $file1UserShareOwner],
1370
+                    ],
1371
+                ],
1372
+                [
1373
+                ],
1374
+                [
1375
+                    $file1UserShareOwnerExpected,
1376
+                ]
1377
+            ],
1378
+            [
1379
+                [
1380
+                    'node' => $folder,
1381
+                    'subfiles' => 'true',
1382
+                ],
1383
+                [
1384
+                    'file1' => [
1385
+                        IShare::TYPE_USER => [$file1UserShareOwner, $file1UserShareRecipient],
1386
+                    ],
1387
+                ],
1388
+                [
1389
+                ],
1390
+                [
1391
+                    $file1UserShareOwnerExpected
1392
+                ]
1393
+            ],
1394
+            [
1395
+                [
1396
+                    'node' => $folder,
1397
+                    'subfiles' => 'true',
1398
+                ],
1399
+                [
1400
+                    'file1' => [
1401
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1402
+                    ],
1403
+                    'file2' => [
1404
+                        IShare::TYPE_USER => [$file2UserShareOwner],
1405
+                    ],
1406
+                ],
1407
+                [
1408
+                ],
1409
+                [
1410
+                    $file1UserShareInitiatorExpected,
1411
+                    $file1UserShareOtherExpected,
1412
+                    $file2UserShareOwnerExpected,
1413
+                ]
1414
+            ],
1415
+            // This might not happen in a real environment, as the combination
1416
+            // of shares does not seem to be possible on a folder without
1417
+            // resharing rights; if the folder has resharing rights then the
1418
+            // share with others would be included too in the results.
1419
+            [
1420
+                [
1421
+                    'node' => $folder,
1422
+                    'subfiles' => 'true',
1423
+                ],
1424
+                [
1425
+                    'file1' => [
1426
+                        IShare::TYPE_USER => [$file1UserShareRecipient, $file1UserShareInitiator, $file1UserShareOther],
1427
+                    ],
1428
+                ],
1429
+                [
1430
+                ],
1431
+                [
1432
+                    $file1UserShareInitiatorExpected,
1433
+                ]
1434
+            ],
1435
+            [
1436
+                [
1437
+                    'node' => $folder,
1438
+                    'subfiles' => 'true',
1439
+                ],
1440
+                [
1441
+                    'file1' => [
1442
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1443
+                        IShare::TYPE_GROUP => [$file1GroupShareRecipient],
1444
+                    ],
1445
+                ],
1446
+                [
1447
+                ],
1448
+                [
1449
+                    $file1UserShareOwnerExpected,
1450
+                    $file1GroupShareRecipientExpected,
1451
+                ]
1452
+            ],
1453
+            [
1454
+                [
1455
+                    'node' => $folder,
1456
+                    'subfiles' => 'true',
1457
+                ],
1458
+                [
1459
+                    'file1' => [
1460
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1461
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1462
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1463
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1464
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1465
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1466
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1467
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1468
+                    ],
1469
+                ],
1470
+                [
1471
+                ],
1472
+                [
1473
+                    $file1UserShareOwnerExpected,
1474
+                    $file1GroupShareOwnerExpected,
1475
+                    $file1LinkShareOwnerExpected,
1476
+                    $file1EmailShareOwnerExpected,
1477
+                    $file1CircleShareOwnerExpected,
1478
+                    $file1RoomShareOwnerExpected,
1479
+                ]
1480
+            ],
1481
+            [
1482
+                [
1483
+                    'node' => $folder,
1484
+                    'subfiles' => 'true',
1485
+                ],
1486
+                [
1487
+                    'file1' => [
1488
+                        IShare::TYPE_USER => [$file1UserShareOwner],
1489
+                        IShare::TYPE_GROUP => [$file1GroupShareOwner],
1490
+                        IShare::TYPE_LINK => [$file1LinkShareOwner],
1491
+                        IShare::TYPE_EMAIL => [$file1EmailShareOwner],
1492
+                        IShare::TYPE_CIRCLE => [$file1CircleShareOwner],
1493
+                        IShare::TYPE_ROOM => [$file1RoomShareOwner],
1494
+                        IShare::TYPE_REMOTE => [$file1RemoteShareOwner],
1495
+                        IShare::TYPE_REMOTE_GROUP => [$file1RemoteGroupShareOwner],
1496
+                    ],
1497
+                ],
1498
+                [
1499
+                    IShare::TYPE_REMOTE => true,
1500
+                    IShare::TYPE_REMOTE_GROUP => true,
1501
+                ],
1502
+                [
1503
+                    $file1UserShareOwnerExpected,
1504
+                    $file1GroupShareOwnerExpected,
1505
+                    $file1LinkShareOwnerExpected,
1506
+                    $file1EmailShareOwnerExpected,
1507
+                    $file1CircleShareOwnerExpected,
1508
+                    $file1RoomShareOwnerExpected,
1509
+                    $file1RemoteShareOwnerExpected,
1510
+                    $file1RemoteGroupShareOwnerExpected,
1511
+                ]
1512
+            ],
1513
+        ];
1514
+
1515
+        return $data;
1516
+    }
1517
+
1518
+    private function mockSimpleNode(string $class, array $methods): MockObject {
1519
+        $node = $this->createMock($class);
1520
+        foreach ($methods as $method => $return) {
1521
+            if ($method === 'getDirectoryListing') {
1522
+                $return = array_map(
1523
+                    fn ($nodeParams) => $this->mockSimpleNode(...$nodeParams),
1524
+                    $return
1525
+                );
1526
+            }
1527
+            $node->method($method)->willReturn($return);
1528
+        }
1529
+        return $node;
1530
+    }
1531
+
1532
+    #[DataProvider('dataGetShares')]
1533
+    public function testGetShares(array $getSharesParameters, array $shares, array $extraShareTypes, array $expected): void {
1534
+        $shares = array_map(
1535
+            fn ($sharesByType) => array_map(
1536
+                fn ($shareList) => array_map(
1537
+                    function (array $shareParams): IShare {
1538
+                        $share = Server::get(IManager::class)->newShare();
1539
+                        $share->setShareType($shareParams['type'])
1540
+                            ->setSharedBy($shareParams['sharedBy'])
1541
+                            ->setShareOwner($shareParams['owner'])
1542
+                            ->setPermissions(Constants::PERMISSION_READ)
1543
+                            ->setId($shareParams['id']);
1544
+                        if (isset($shareParams['sharedWith'])) {
1545
+                            $share->setSharedWith($shareParams['sharedWith']);
1546
+                        }
1547
+                        if (isset($shareParams['sharedWithDisplayName'])) {
1548
+                            $share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
1549
+                        }
1550
+                        if (isset($shareParams['sharedWithAvatar'])) {
1551
+                            $share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
1552
+                        }
1553
+                        if (isset($shareParams['attributes'])) {
1554
+                            $shareAttributes = $this->createMock(IShareAttributes::class);
1555
+                            $shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
1556
+                            $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
1557
+                            $share->setAttributes($shareAttributes);
1558
+
1559
+                            $expects['attributes'] = \json_encode($shareParams['attributes']);
1560
+                        }
1561
+                        if (isset($shareParams['node'])) {
1562
+                            $node = $this->mockSimpleNode(...$shareParams['node']);
1563
+                            $share->setNode($node);
1564
+                        }
1565
+                        if (isset($shareParams['note'])) {
1566
+                            $share->setNote($shareParams['note']);
1567
+                        }
1568
+                        if (isset($shareParams['expirationDate'])) {
1569
+                            $share->setExpirationDate($shareParams['expirationDate']);
1570
+                        }
1571
+                        if (isset($shareParams['token'])) {
1572
+                            $share->setToken($shareParams['token']);
1573
+                        }
1574
+                        if (isset($shareParams['label'])) {
1575
+                            $share->setLabel($shareParams['label']);
1576
+                        }
1577
+                        if (isset($shareParams['password'])) {
1578
+                            $share->setPassword($shareParams['password']);
1579
+                        }
1580
+                        if (isset($shareParams['sendPasswordByTalk'])) {
1581
+                            $share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
1582
+                        }
1583
+                        return $share;
1584
+                    },
1585
+                    $shareList
1586
+                ),
1587
+                $sharesByType
1588
+            ),
1589
+            $shares
1590
+        );
1591
+
1592
+        /** @var ShareAPIController&MockObject $ocs */
1593
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
1594
+            ->setConstructorArgs([
1595
+                $this->appName,
1596
+                $this->request,
1597
+                $this->shareManager,
1598
+                $this->groupManager,
1599
+                $this->userManager,
1600
+                $this->rootFolder,
1601
+                $this->urlGenerator,
1602
+                $this->l,
1603
+                $this->config,
1604
+                $this->appConfig,
1605
+                $this->appManager,
1606
+                $this->serverContainer,
1607
+                $this->userStatusManager,
1608
+                $this->previewManager,
1609
+                $this->dateTimeZone,
1610
+                $this->logger,
1611
+                $this->factory,
1612
+                $this->mailer,
1613
+                $this->tagManager,
1614
+                $this->getEmailValidatorWithStrictEmailCheck(),
1615
+                $this->trustedServers,
1616
+                $this->currentUser,
1617
+            ])
1618
+            ->onlyMethods(['formatShare'])
1619
+            ->getMock();
1620
+
1621
+        $ocs->method('formatShare')
1622
+            ->willReturnCallback(
1623
+                function ($share) {
1624
+                    return [
1625
+                        'id' => $share->getId(),
1626
+                        'share_type' => $share->getShareType()
1627
+                    ];
1628
+                }
1629
+            );
1630
+
1631
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1632
+        $userFolder->method('get')
1633
+            ->with('path')
1634
+            ->willReturn($this->mockSimpleNode(...$getSharesParameters['node']));
1635
+
1636
+        $this->rootFolder->method('getUserFolder')
1637
+            ->with($this->currentUser)
1638
+            ->willReturn($userFolder);
1639
+
1640
+        $this->shareManager
1641
+            ->method('getSharesBy')
1642
+            ->willReturnCallback(
1643
+                function ($user, $shareType, $node) use ($shares) {
1644
+                    if (!isset($shares[$node->getName()]) || !isset($shares[$node->getName()][$shareType])) {
1645
+                        return [];
1646
+                    }
1647
+                    return $shares[$node->getName()][$shareType];
1648
+                }
1649
+            );
1650
+
1651
+        $this->shareManager
1652
+            ->method('outgoingServer2ServerSharesAllowed')
1653
+            ->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE] ?? false);
1654
+
1655
+        $this->shareManager
1656
+            ->method('outgoingServer2ServerGroupSharesAllowed')
1657
+            ->willReturn($extraShareTypes[ISHARE::TYPE_REMOTE_GROUP] ?? false);
1658
+
1659
+        $this->groupManager
1660
+            ->method('isInGroup')
1661
+            ->willReturnCallback(
1662
+                function ($user, $group) {
1663
+                    return $group === 'currentUserGroup';
1664
+                }
1665
+            );
1666
+
1667
+        $result = $ocs->getShares(
1668
+            $getSharesParameters['sharedWithMe'] ?? 'false',
1669
+            $getSharesParameters['reshares'] ?? 'false',
1670
+            $getSharesParameters['subfiles'] ?? 'false',
1671
+            'path'
1672
+        );
1673
+
1674
+        $this->assertEquals($expected, $result->getData());
1675
+    }
1676
+
1677
+    public function testCanAccessShareAsOwner(): void {
1678
+        $share = $this->createMock(IShare::class);
1679
+        $share->method('getShareOwner')->willReturn($this->currentUser);
1680
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1681
+    }
1682
+
1683
+    public function testCanAccessShareAsSharer(): void {
1684
+        $share = $this->createMock(IShare::class);
1685
+        $share->method('getSharedBy')->willReturn($this->currentUser);
1686
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1687
+    }
1688
+
1689
+    public function testCanAccessShareAsSharee(): void {
1690
+        $share = $this->createMock(IShare::class);
1691
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
1692
+        $share->method('getSharedWith')->willReturn($this->currentUser);
1693
+        $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1694
+    }
1695
+
1696
+    public function testCannotAccessLinkShare(): void {
1697
+        $share = $this->createMock(IShare::class);
1698
+        $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
1699
+        $share->method('getNodeId')->willReturn(42);
1700
+
1701
+        $userFolder = $this->createMock(Folder::class);
1702
+        $this->rootFolder->method('getUserFolder')
1703
+            ->with($this->currentUser)
1704
+            ->willReturn($userFolder);
1705
+
1706
+        $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1707
+    }
1708
+
1709
+    #[DataProvider('dataCanAccessShareWithPermissions')]
1710
+    public function testCanAccessShareWithPermissions(int $permissions, bool $expected): void {
1711
+        $share = $this->createMock(IShare::class);
1712
+        $share->method('getShareType')->willReturn(IShare::TYPE_USER);
1713
+        $share->method('getSharedWith')->willReturn($this->createMock(IUser::class));
1714
+        $share->method('getNodeId')->willReturn(42);
1715
+
1716
+        $file = $this->createMock(File::class);
1717
+
1718
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1719
+        $userFolder->method('getFirstNodeById')
1720
+            ->with($share->getNodeId())
1721
+            ->willReturn($file);
1722
+        $userFolder->method('getById')
1723
+            ->with($share->getNodeId())
1724
+            ->willReturn([$file]);
1725
+        $this->rootFolder->method('getUserFolder')
1726
+            ->with($this->currentUser)
1727
+            ->willReturn($userFolder);
1728
+
1729
+        $file->method('getPermissions')
1730
+            ->willReturn($permissions);
1731
+
1732
+        if ($expected) {
1733
+            $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1734
+        } else {
1735
+            $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1736
+        }
1737
+    }
1738
+
1739
+    public static function dataCanAccessShareWithPermissions(): array {
1740
+        return [
1741
+            [Constants::PERMISSION_SHARE, true],
1742
+            [Constants::PERMISSION_READ, false],
1743
+            [Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, true],
1744
+        ];
1745
+    }
1746
+
1747
+    #[DataProvider('dataCanAccessShareAsGroupMember')]
1748
+    public function testCanAccessShareAsGroupMember(string $group, bool $expected): void {
1749
+        $share = $this->createMock(IShare::class);
1750
+        $share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
1751
+        $share->method('getSharedWith')->willReturn($group);
1752
+        $share->method('getNodeId')->willReturn(42);
1753
+
1754
+        $file = $this->createMock(File::class);
1755
+
1756
+        $userFolder = $this->createMock(Folder::class);
1757
+        $userFolder->method('getFirstNodeById')
1758
+            ->with($share->getNodeId())
1759
+            ->willReturn($file);
1760
+        $userFolder->method('getById')
1761
+            ->with($share->getNodeId())
1762
+            ->willReturn([$file]);
1763
+        $this->rootFolder->method('getUserFolder')
1764
+            ->with($this->currentUser)
1765
+            ->willReturn($userFolder);
1766
+
1767
+        $user = $this->createMock(IUser::class);
1768
+        $this->userManager->method('get')
1769
+            ->with($this->currentUser)
1770
+            ->willReturn($user);
1771
+
1772
+        $group = $this->createMock(IGroup::class);
1773
+        $group->method('inGroup')->with($user)->willReturn(true);
1774
+        $group2 = $this->createMock(IGroup::class);
1775
+        $group2->method('inGroup')->with($user)->willReturn(false);
1776
+
1777
+        $this->groupManager->method('get')->willReturnMap([
1778
+            ['group', $group],
1779
+            ['group2', $group2],
1780
+            ['group-null', null],
1781
+        ]);
1782
+
1783
+        if ($expected) {
1784
+            $this->assertTrue($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1785
+        } else {
1786
+            $this->assertFalse($this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1787
+        }
1788
+    }
1789
+
1790
+    public static function dataCanAccessShareAsGroupMember(): array {
1791
+        return [
1792
+            ['group', true],
1793
+            ['group2', false],
1794
+            ['group-null', false],
1795
+        ];
1796
+    }
1797
+
1798
+    public static function dataCanAccessRoomShare(): array {
1799
+        return [
1800
+            [false, false, false],
1801
+            [false, false, true],
1802
+            [true, true, true],
1803
+            [false, true, false],
1804
+        ];
1805
+    }
1806
+
1807
+    #[DataProvider('dataCanAccessRoomShare')]
1808
+    public function testCanAccessRoomShare(
1809
+        bool $expected,
1810
+        bool $helperAvailable,
1811
+        bool $canAccessShareByHelper,
1812
+    ): void {
1813
+        $share = $this->createMock(IShare::class);
1814
+        $share->method('getShareType')->willReturn(IShare::TYPE_ROOM);
1815
+        $share->method('getSharedWith')->willReturn('recipientRoom');
1816
+
1817
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1818
+        $this->rootFolder->method('getUserFolder')
1819
+            ->with($this->currentUser)
1820
+            ->willReturn($userFolder);
1821
+
1822
+        $userFolder->method('getById')
1823
+            ->with($share->getNodeId())
1824
+            ->willReturn([$share->getNode()]);
1825
+
1826
+        if (!$helperAvailable) {
1827
+            $this->appManager->method('isEnabledForUser')
1828
+                ->with('spreed')
1829
+                ->willReturn(false);
1830
+        } else {
1831
+            $this->appManager->method('isEnabledForUser')
1832
+                ->with('spreed')
1833
+                ->willReturn(true);
1834
+
1835
+            // This is not possible anymore with PHPUnit 10+
1836
+            // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
1837
+            // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
1838
+            $helper = $this->getMockBuilder(\stdClass::class)
1839
+                ->addMethods(['canAccessShare'])
1840
+                ->getMock();
1841
+            $helper->method('canAccessShare')
1842
+                ->with($share, $this->currentUser)
1843
+                ->willReturn($canAccessShareByHelper);
1844
+
1845
+            $this->serverContainer->method('get')
1846
+                ->with('\OCA\Talk\Share\Helper\ShareAPIController')
1847
+                ->willReturn($helper);
1848
+        }
1849
+
1850
+        $this->assertEquals($expected, $this->invokePrivate($this->ocs, 'canAccessShare', [$share]));
1851
+    }
1852
+
1853
+
1854
+    public function testCreateShareNoPath(): void {
1855
+        $this->expectException(OCSNotFoundException::class);
1856
+        $this->expectExceptionMessage('Please specify a file or folder path');
1857
+
1858
+        $this->ocs->createShare();
1859
+    }
1860
+
1861
+
1862
+    public function testCreateShareInvalidPath(): void {
1863
+        $this->expectException(OCSNotFoundException::class);
1864
+        $this->expectExceptionMessage('Wrong path, file/folder does not exist');
1865
+
1866
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
1867
+        $this->rootFolder->expects($this->once())
1868
+            ->method('getUserFolder')
1869
+            ->with('currentUser')
1870
+            ->willReturn($userFolder);
1871
+
1872
+        $userFolder->expects($this->once())
1873
+            ->method('get')
1874
+            ->with('invalid-path')
1875
+            ->willThrowException(new NotFoundException());
1876
+
1877
+        $this->ocs->createShare('invalid-path');
1878
+    }
1879
+
1880
+    public function testCreateShareInvalidShareType(): void {
1881
+        $this->expectException(OCSBadRequestException::class);
1882
+        $this->expectExceptionMessage('Unknown share type');
1883
+
1884
+        $share = $this->newShare();
1885
+        $this->shareManager->method('newShare')->willReturn($share);
1886
+
1887
+        [$userFolder, $file] = $this->getNonSharedUserFile();
1888
+        $this->rootFolder->expects($this->atLeastOnce())
1889
+            ->method('getUserFolder')
1890
+            ->with('currentUser')
1891
+            ->willReturn($userFolder);
1892
+
1893
+        $userFolder->expects($this->atLeastOnce())
1894
+            ->method('get')
1895
+            ->with('valid-path')
1896
+            ->willReturn($file);
1897
+        $userFolder->method('getById')
1898
+            ->willReturn([]);
1899
+
1900
+        $file->expects($this->once())
1901
+            ->method('lock')
1902
+            ->with(ILockingProvider::LOCK_SHARED);
1903
+
1904
+        $this->ocs->createShare('valid-path', 31);
1905
+    }
1906
+
1907
+    public function testCreateShareUserNoShareWith(): void {
1908
+        $this->expectException(OCSNotFoundException::class);
1909
+        $this->expectExceptionMessage('Please specify a valid account to share with');
1910
+
1911
+        $share = $this->newShare();
1912
+        $this->shareManager->method('newShare')->willReturn($share);
1913
+
1914
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1915
+        $this->rootFolder->method('getUserFolder')
1916
+            ->with('currentUser')
1917
+            ->willReturn($userFolder);
1918
+
1919
+        $userFolder->expects($this->once())
1920
+            ->method('get')
1921
+            ->with('valid-path')
1922
+            ->willReturn($path);
1923
+        $userFolder->method('getById')
1924
+            ->willReturn([]);
1925
+
1926
+        $path->expects($this->once())
1927
+            ->method('lock')
1928
+            ->with(ILockingProvider::LOCK_SHARED);
1929
+
1930
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER);
1931
+    }
1932
+
1933
+
1934
+    public function testCreateShareUserNoValidShareWith(): void {
1935
+        $this->expectException(OCSNotFoundException::class);
1936
+        $this->expectExceptionMessage('Please specify a valid account to share with');
1937
+
1938
+        $share = $this->newShare();
1939
+        $this->shareManager->method('newShare')->willReturn($share);
1940
+
1941
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1942
+        $this->rootFolder->method('getUserFolder')
1943
+            ->with('currentUser')
1944
+            ->willReturn($userFolder);
1945
+
1946
+        $userFolder->expects($this->once())
1947
+            ->method('get')
1948
+            ->with('valid-path')
1949
+            ->willReturn($path);
1950
+        $userFolder->method('getById')
1951
+            ->willReturn([]);
1952
+        $path->expects($this->once())
1953
+            ->method('lock')
1954
+            ->with(ILockingProvider::LOCK_SHARED);
1955
+        $this->userManager->method('userExists')
1956
+            ->with('invalidUser')
1957
+            ->willReturn(false);
1958
+
1959
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'invalidUser');
1960
+    }
1961
+
1962
+    public function testCreateShareUser(): void {
1963
+        $share = $this->newShare();
1964
+        $this->shareManager->method('newShare')->willReturn($share);
1965
+
1966
+        /** @var ShareAPIController $ocs */
1967
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
1968
+            ->setConstructorArgs([
1969
+                $this->appName,
1970
+                $this->request,
1971
+                $this->shareManager,
1972
+                $this->groupManager,
1973
+                $this->userManager,
1974
+                $this->rootFolder,
1975
+                $this->urlGenerator,
1976
+                $this->l,
1977
+                $this->config,
1978
+                $this->appConfig,
1979
+                $this->appManager,
1980
+                $this->serverContainer,
1981
+                $this->userStatusManager,
1982
+                $this->previewManager,
1983
+                $this->dateTimeZone,
1984
+                $this->logger,
1985
+                $this->factory,
1986
+                $this->mailer,
1987
+                $this->tagManager,
1988
+                $this->getEmailValidatorWithStrictEmailCheck(),
1989
+                $this->trustedServers,
1990
+                $this->currentUser,
1991
+            ])->onlyMethods(['formatShare'])
1992
+            ->getMock();
1993
+
1994
+        [$userFolder, $path] = $this->getNonSharedUserFile();
1995
+        $this->rootFolder->expects($this->exactly(2))
1996
+            ->method('getUserFolder')
1997
+            ->with('currentUser')
1998
+            ->willReturn($userFolder);
1999
+
2000
+        $userFolder->expects($this->once())
2001
+            ->method('get')
2002
+            ->with('valid-path')
2003
+            ->willReturn($path);
2004
+        $userFolder->method('getById')
2005
+            ->willReturn([]);
2006
+
2007
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2008
+
2009
+        $path->expects($this->once())
2010
+            ->method('lock')
2011
+            ->with(ILockingProvider::LOCK_SHARED);
2012
+
2013
+        $this->shareManager->method('createShare')
2014
+            ->with($this->callback(function (IShare $share) use ($path) {
2015
+                return $share->getNode() === $path
2016
+                    && $share->getPermissions() === (
2017
+                        Constants::PERMISSION_ALL
2018
+                        & ~Constants::PERMISSION_DELETE
2019
+                        & ~Constants::PERMISSION_CREATE
2020
+                    )
2021
+                    && $share->getShareType() === IShare::TYPE_USER
2022
+                    && $share->getSharedWith() === 'validUser'
2023
+                    && $share->getSharedBy() === 'currentUser';
2024
+            }))
2025
+            ->willReturnArgument(0);
2026
+
2027
+        $expected = new DataResponse([]);
2028
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2029
+
2030
+        $this->assertInstanceOf(get_class($expected), $result);
2031
+        $this->assertEquals($expected->getData(), $result->getData());
2032
+    }
2033
+
2034
+
2035
+    public function testCreateShareGroupNoValidShareWith(): void {
2036
+        $this->expectException(OCSNotFoundException::class);
2037
+        $this->expectExceptionMessage('Please specify a valid group');
2038
+
2039
+        $share = $this->newShare();
2040
+        $this->shareManager->method('newShare')->willReturn($share);
2041
+        $this->shareManager->method('createShare')->willReturnArgument(0);
2042
+        $this->shareManager->method('allowGroupSharing')->willReturn(true);
2043
+
2044
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2045
+        $this->rootFolder->method('getUserFolder')
2046
+            ->with('currentUser')
2047
+            ->willReturn($userFolder);
2048
+
2049
+        $userFolder->expects($this->once())
2050
+            ->method('get')
2051
+            ->with('valid-path')
2052
+            ->willReturn($path);
2053
+        $userFolder->method('getById')
2054
+            ->willReturn([]);
2055
+
2056
+        $path->expects($this->once())
2057
+            ->method('lock')
2058
+            ->with(ILockingProvider::LOCK_SHARED);
2059
+
2060
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2061
+    }
2062
+
2063
+    public function testCreateShareGroup(): void {
2064
+        $share = $this->newShare();
2065
+        $this->shareManager->method('newShare')->willReturn($share);
2066
+
2067
+        /** @var ShareAPIController&MockObject $ocs */
2068
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2069
+            ->setConstructorArgs([
2070
+                $this->appName,
2071
+                $this->request,
2072
+                $this->shareManager,
2073
+                $this->groupManager,
2074
+                $this->userManager,
2075
+                $this->rootFolder,
2076
+                $this->urlGenerator,
2077
+                $this->l,
2078
+                $this->config,
2079
+                $this->appConfig,
2080
+                $this->appManager,
2081
+                $this->serverContainer,
2082
+                $this->userStatusManager,
2083
+                $this->previewManager,
2084
+                $this->dateTimeZone,
2085
+                $this->logger,
2086
+                $this->factory,
2087
+                $this->mailer,
2088
+                $this->tagManager,
2089
+                $this->getEmailValidatorWithStrictEmailCheck(),
2090
+                $this->trustedServers,
2091
+                $this->currentUser,
2092
+            ])->onlyMethods(['formatShare'])
2093
+            ->getMock();
2094
+
2095
+        $this->request
2096
+            ->method('getParam')
2097
+            ->willReturnMap([
2098
+                ['path', null, 'valid-path'],
2099
+                ['permissions', null, Constants::PERMISSION_ALL],
2100
+                ['shareType', '-1', IShare::TYPE_GROUP],
2101
+                ['shareWith', null, 'validGroup'],
2102
+            ]);
2103
+
2104
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2105
+        $this->rootFolder->expects($this->exactly(2))
2106
+            ->method('getUserFolder')
2107
+            ->with('currentUser')
2108
+            ->willReturn($userFolder);
2109
+
2110
+        $userFolder->expects($this->once())
2111
+            ->method('get')
2112
+            ->with('valid-path')
2113
+            ->willReturn($path);
2114
+        $userFolder->method('getById')
2115
+            ->willReturn([]);
2116
+
2117
+        $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2118
+
2119
+        $this->shareManager->expects($this->once())
2120
+            ->method('allowGroupSharing')
2121
+            ->willReturn(true);
2122
+
2123
+        $path->expects($this->once())
2124
+            ->method('lock')
2125
+            ->with(ILockingProvider::LOCK_SHARED);
2126
+
2127
+        $this->shareManager->method('createShare')
2128
+            ->with($this->callback(function (IShare $share) use ($path) {
2129
+                return $share->getNode() === $path
2130
+                && $share->getPermissions() === Constants::PERMISSION_ALL
2131
+                && $share->getShareType() === IShare::TYPE_GROUP
2132
+                && $share->getSharedWith() === 'validGroup'
2133
+                && $share->getSharedBy() === 'currentUser';
2134
+            }))
2135
+            ->willReturnArgument(0);
2136
+
2137
+        $expected = new DataResponse([]);
2138
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'validGroup');
2139
+
2140
+        $this->assertInstanceOf(get_class($expected), $result);
2141
+        $this->assertEquals($expected->getData(), $result->getData());
2142
+    }
2143
+
2144
+
2145
+    public function testCreateShareGroupNotAllowed(): void {
2146
+        $this->expectException(OCSNotFoundException::class);
2147
+        $this->expectExceptionMessage('Group sharing is disabled by the administrator');
2148
+
2149
+        $share = $this->newShare();
2150
+        $this->shareManager->method('newShare')->willReturn($share);
2151
+
2152
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2153
+        $this->rootFolder->method('getUserFolder')
2154
+            ->with('currentUser')
2155
+            ->willReturn($userFolder);
2156
+
2157
+        $userFolder->expects($this->once())
2158
+            ->method('get')
2159
+            ->with('valid-path')
2160
+            ->willReturn($path);
2161
+        $userFolder->method('getById')
2162
+            ->willReturn([]);
2163
+
2164
+        $this->groupManager->method('groupExists')->with('validGroup')->willReturn(true);
2165
+
2166
+        $this->shareManager->expects($this->once())
2167
+            ->method('allowGroupSharing')
2168
+            ->willReturn(false);
2169
+
2170
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_GROUP, 'invalidGroup');
2171
+    }
2172
+
2173
+
2174
+    public function testCreateShareLinkNoLinksAllowed(): void {
2175
+        $this->expectException(OCSNotFoundException::class);
2176
+        $this->expectExceptionMessage('Public link sharing is disabled by the administrator');
2177
+
2178
+        $this->request
2179
+            ->method('getParam')
2180
+            ->willReturnMap([
2181
+                ['path', null, 'valid-path'],
2182
+                ['shareType', '-1', IShare::TYPE_LINK],
2183
+            ]);
2184
+
2185
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2186
+        $path->method('getId')->willReturn(42);
2187
+        $storage = $this->createMock(IStorage::class);
2188
+        $storage->method('instanceOfStorage')
2189
+            ->willReturnMap([
2190
+                ['OCA\Files_Sharing\External\Storage', false],
2191
+                ['OCA\Files_Sharing\SharedStorage', false],
2192
+            ]);
2193
+        $path->method('getStorage')->willReturn($storage);
2194
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2195
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2196
+        $this->rootFolder->method('getById')
2197
+            ->willReturn([]);
2198
+
2199
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2200
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2201
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(false);
2202
+
2203
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK);
2204
+    }
2205
+
2206
+
2207
+    public function testCreateShareLinkNoPublicUpload(): void {
2208
+        $this->expectException(OCSForbiddenException::class);
2209
+        $this->expectExceptionMessage('Public upload disabled by the administrator');
2210
+
2211
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2212
+        $path->method('getId')->willReturn(42);
2213
+        $storage = $this->createMock(IStorage::class);
2214
+        $storage->method('instanceOfStorage')
2215
+            ->willReturnMap([
2216
+                ['OCA\Files_Sharing\External\Storage', false],
2217
+                ['OCA\Files_Sharing\SharedStorage', false],
2218
+            ]);
2219
+        $path->method('getStorage')->willReturn($storage);
2220
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2221
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2222
+        $this->rootFolder->method('getById')
2223
+            ->willReturn([]);
2224
+
2225
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2226
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2227
+
2228
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2229
+    }
2230
+
2231
+
2232
+    public function testCreateShareLinkPublicUploadFile(): void {
2233
+        $this->expectException(OCSBadRequestException::class);
2234
+        $this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
2235
+
2236
+        $storage = $this->createMock(IStorage::class);
2237
+        $storage->method('instanceOfStorage')
2238
+            ->willReturnMap([
2239
+                ['OCA\Files_Sharing\External\Storage', false],
2240
+                ['OCA\Files_Sharing\SharedStorage', false],
2241
+            ]);
2242
+
2243
+        $file = $this->createMock(File::class);
2244
+        $file->method('getId')->willReturn(42);
2245
+        $file->method('getStorage')->willReturn($storage);
2246
+
2247
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2248
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($file);
2249
+        $this->rootFolder->method('getById')
2250
+            ->willReturn([]);
2251
+
2252
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2253
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2254
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2255
+
2256
+        $this->ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
2257
+    }
2258
+
2259
+    public function testCreateShareLinkPublicUploadFolder(): void {
2260
+        $ocs = $this->mockFormatShare();
2261
+
2262
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2263
+        $path->method('getId')->willReturn(1);
2264
+        $storage = $this->createMock(IStorage::class);
2265
+        $storage->method('instanceOfStorage')
2266
+            ->willReturnMap([
2267
+                ['OCA\Files_Sharing\External\Storage', false],
2268
+                ['OCA\Files_Sharing\SharedStorage', false],
2269
+            ]);
2270
+        $path->method('getStorage')->willReturn($storage);
2271
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2272
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2273
+        $this->rootFolder->method('getById')
2274
+            ->willReturn([]);
2275
+
2276
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2277
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2278
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2279
+
2280
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2281
+            $this->callback(function (IShare $share) use ($path) {
2282
+                return $share->getNode() === $path
2283
+                    && $share->getShareType() === IShare::TYPE_LINK
2284
+                    && $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
2285
+                    && $share->getSharedBy() === 'currentUser'
2286
+                    && $share->getPassword() === null
2287
+                    && $share->getExpirationDate() === null;
2288
+            })
2289
+        )->willReturnArgument(0);
2290
+
2291
+        $expected = new DataResponse([]);
2292
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', '', null, '');
2293
+
2294
+        $this->assertInstanceOf(get_class($expected), $result);
2295
+        $this->assertEquals($expected->getData(), $result->getData());
2296
+    }
2297
+
2298
+    public function testCreateShareLinkPassword(): void {
2299
+        $ocs = $this->mockFormatShare();
2300
+
2301
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2302
+        $path->method('getId')->willReturn(42);
2303
+        $storage = $this->createMock(IStorage::class);
2304
+        $storage->method('instanceOfStorage')
2305
+            ->willReturnMap([
2306
+                ['OCA\Files_Sharing\External\Storage', false],
2307
+                ['OCA\Files_Sharing\SharedStorage', false],
2308
+            ]);
2309
+        $path->method('getStorage')->willReturn($storage);
2310
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2311
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2312
+        $this->rootFolder->method('getById')
2313
+            ->willReturn([]);
2314
+
2315
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2316
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2317
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2318
+
2319
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2320
+            $this->callback(function (IShare $share) use ($path) {
2321
+                return $share->getNode() === $path
2322
+                && $share->getShareType() === IShare::TYPE_LINK
2323
+                && $share->getPermissions() === Constants::PERMISSION_READ // publicUpload was set to false
2324
+                && $share->getSharedBy() === 'currentUser'
2325
+                && $share->getPassword() === 'password'
2326
+                && $share->getExpirationDate() === null;
2327
+            })
2328
+        )->willReturnArgument(0);
2329
+
2330
+        $expected = new DataResponse([]);
2331
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_READ, IShare::TYPE_LINK, null, 'false', 'password', null, '');
2332
+
2333
+        $this->assertInstanceOf(get_class($expected), $result);
2334
+        $this->assertEquals($expected->getData(), $result->getData());
2335
+    }
2336
+
2337
+    public function testCreateShareLinkSendPasswordByTalk(): void {
2338
+        $ocs = $this->mockFormatShare();
2339
+
2340
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2341
+        $path->method('getId')->willReturn(42);
2342
+        $storage = $this->createMock(IStorage::class);
2343
+        $storage->method('instanceOfStorage')
2344
+            ->willReturnMap([
2345
+                ['OCA\Files_Sharing\External\Storage', false],
2346
+                ['OCA\Files_Sharing\SharedStorage', false],
2347
+            ]);
2348
+        $path->method('getStorage')->willReturn($storage);
2349
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2350
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2351
+        $this->rootFolder->method('getById')
2352
+            ->willReturn([]);
2353
+
2354
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2355
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2356
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2357
+
2358
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
2359
+
2360
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2361
+            $this->callback(function (IShare $share) use ($path) {
2362
+                return $share->getNode() === $path
2363
+                && $share->getShareType() === IShare::TYPE_LINK
2364
+                && $share->getPermissions() === (Constants::PERMISSION_ALL & ~(Constants::PERMISSION_SHARE))
2365
+                && $share->getSharedBy() === 'currentUser'
2366
+                && $share->getPassword() === 'password'
2367
+                && $share->getSendPasswordByTalk() === true
2368
+                && $share->getExpirationDate() === null;
2369
+            })
2370
+        )->willReturnArgument(0);
2371
+
2372
+        $expected = new DataResponse([]);
2373
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true', 'password', 'true', '');
2374
+
2375
+        $this->assertInstanceOf(get_class($expected), $result);
2376
+        $this->assertEquals($expected->getData(), $result->getData());
2377
+    }
2378
+
2379
+
2380
+    public function testCreateShareLinkSendPasswordByTalkWithTalkDisabled(): void {
2381
+        $this->expectException(OCSForbiddenException::class);
2382
+        $this->expectExceptionMessage('Sharing valid-path sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled');
2383
+
2384
+        $ocs = $this->mockFormatShare();
2385
+
2386
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2387
+        $path->method('getId')->willReturn(42);
2388
+        $storage = $this->createMock(IStorage::class);
2389
+        $storage->method('instanceOfStorage')
2390
+            ->willReturnMap([
2391
+                ['OCA\Files_Sharing\External\Storage', false],
2392
+                ['OCA\Files_Sharing\SharedStorage', false],
2393
+            ]);
2394
+        $path->method('getStorage')->willReturn($storage);
2395
+        $path->method('getPath')->willReturn('valid-path');
2396
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2397
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2398
+        $this->rootFolder->method('getById')
2399
+            ->willReturn([]);
2400
+
2401
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2402
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2403
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2404
+
2405
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
2406
+
2407
+        $this->shareManager->expects($this->never())->method('createShare');
2408
+
2409
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', 'password', 'true', '');
2410
+    }
2411
+
2412
+    public function testCreateShareValidExpireDate(): void {
2413
+        $ocs = $this->mockFormatShare();
2414
+
2415
+        $this->request
2416
+            ->method('getParam')
2417
+            ->willReturnMap([
2418
+                ['path', null, 'valid-path'],
2419
+                ['shareType', '-1', IShare::TYPE_LINK],
2420
+                ['publicUpload', null, 'false'],
2421
+                ['expireDate', '', '2000-01-01'],
2422
+                ['password', '', ''],
2423
+            ]);
2424
+
2425
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2426
+        $path->method('getId')->willReturn(42);
2427
+        $storage = $this->createMock(IStorage::class);
2428
+        $storage->method('instanceOfStorage')
2429
+            ->willReturnMap([
2430
+                ['OCA\Files_Sharing\External\Storage', false],
2431
+                ['OCA\Files_Sharing\SharedStorage', false],
2432
+            ]);
2433
+        $path->method('getStorage')->willReturn($storage);
2434
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2435
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2436
+        $this->rootFolder->method('getById')
2437
+            ->willReturn([]);
2438
+
2439
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2440
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2441
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2442
+
2443
+        $this->shareManager->expects($this->once())->method('createShare')->with(
2444
+            $this->callback(function (IShare $share) use ($path) {
2445
+                $date = new \DateTime('2000-01-01');
2446
+                $date->setTime(0, 0, 0);
2447
+
2448
+                return $share->getNode() === $path
2449
+                && $share->getShareType() === IShare::TYPE_LINK
2450
+                && $share->getPermissions() === Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
2451
+                && $share->getSharedBy() === 'currentUser'
2452
+                && $share->getPassword() === null
2453
+                && $share->getExpirationDate() == $date;
2454
+            })
2455
+        )->willReturnArgument(0);
2456
+
2457
+        $expected = new DataResponse([]);
2458
+        $result = $ocs->createShare('valid-path', null, IShare::TYPE_LINK, null, 'false', '', null, '2000-01-01');
2459
+
2460
+        $this->assertInstanceOf(get_class($expected), $result);
2461
+        $this->assertEquals($expected->getData(), $result->getData());
2462
+    }
2463
+
2464
+
2465
+    public function testCreateShareInvalidExpireDate(): void {
2466
+        $this->expectException(OCSNotFoundException::class);
2467
+        $this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
2468
+
2469
+        $ocs = $this->mockFormatShare();
2470
+
2471
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2472
+        $path->method('getId')->willReturn(42);
2473
+        $storage = $this->createMock(IStorage::class);
2474
+        $storage->method('instanceOfStorage')
2475
+            ->willReturnMap([
2476
+                ['OCA\Files_Sharing\External\Storage', false],
2477
+                ['OCA\Files_Sharing\SharedStorage', false],
2478
+            ]);
2479
+        $path->method('getStorage')->willReturn($storage);
2480
+        $this->rootFolder->method('getUserFolder')->with($this->currentUser)->willReturnSelf();
2481
+        $this->rootFolder->method('get')->with('valid-path')->willReturn($path);
2482
+        $this->rootFolder->method('getById')
2483
+            ->willReturn([]);
2484
+
2485
+        $this->shareManager->method('newShare')->willReturn(Server::get(IManager::class)->newShare());
2486
+        $this->shareManager->method('shareApiAllowLinks')->willReturn(true);
2487
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
2488
+
2489
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, 'a1b2d3');
2490
+    }
2491
+
2492
+    public function testCreateShareRemote(): void {
2493
+        $share = $this->newShare();
2494
+        $this->shareManager->method('newShare')->willReturn($share);
2495
+
2496
+        /** @var ShareAPIController $ocs */
2497
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2498
+            ->setConstructorArgs([
2499
+                $this->appName,
2500
+                $this->request,
2501
+                $this->shareManager,
2502
+                $this->groupManager,
2503
+                $this->userManager,
2504
+                $this->rootFolder,
2505
+                $this->urlGenerator,
2506
+                $this->l,
2507
+                $this->config,
2508
+                $this->appConfig,
2509
+                $this->appManager,
2510
+                $this->serverContainer,
2511
+                $this->userStatusManager,
2512
+                $this->previewManager,
2513
+                $this->dateTimeZone,
2514
+                $this->logger,
2515
+                $this->factory,
2516
+                $this->mailer,
2517
+                $this->tagManager,
2518
+                $this->getEmailValidatorWithStrictEmailCheck(),
2519
+                $this->trustedServers,
2520
+                $this->currentUser,
2521
+            ])->onlyMethods(['formatShare'])
2522
+            ->getMock();
2523
+
2524
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2525
+        $this->rootFolder->expects($this->exactly(2))
2526
+            ->method('getUserFolder')
2527
+            ->with('currentUser')
2528
+            ->willReturn($userFolder);
2529
+
2530
+        $userFolder->expects($this->once())
2531
+            ->method('get')
2532
+            ->with('valid-path')
2533
+            ->willReturn($path);
2534
+        $userFolder->method('getById')
2535
+            ->willReturn([]);
2536
+
2537
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2538
+
2539
+        $path->expects($this->once())
2540
+            ->method('lock')
2541
+            ->with(ILockingProvider::LOCK_SHARED);
2542
+
2543
+        $this->shareManager->method('createShare')
2544
+            ->with($this->callback(function (IShare $share) use ($path) {
2545
+                return $share->getNode() === $path
2546
+                    && $share->getPermissions() === (
2547
+                        Constants::PERMISSION_ALL
2548
+                        & ~Constants::PERMISSION_DELETE
2549
+                        & ~Constants::PERMISSION_CREATE
2550
+                    )
2551
+                    && $share->getShareType() === IShare::TYPE_REMOTE
2552
+                    && $share->getSharedWith() === '[email protected]'
2553
+                    && $share->getSharedBy() === 'currentUser';
2554
+            }))
2555
+            ->willReturnArgument(0);
2556
+
2557
+        $this->shareManager->method('outgoingServer2ServerSharesAllowed')->willReturn(true);
2558
+
2559
+        $expected = new DataResponse([]);
2560
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE, '[email protected]');
2561
+
2562
+        $this->assertInstanceOf(get_class($expected), $result);
2563
+        $this->assertEquals($expected->getData(), $result->getData());
2564
+    }
2565
+
2566
+    public function testCreateShareRemoteGroup(): void {
2567
+        $share = $this->newShare();
2568
+        $this->shareManager->method('newShare')->willReturn($share);
2569
+
2570
+        /** @var ShareAPIController $ocs */
2571
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2572
+            ->setConstructorArgs([
2573
+                $this->appName,
2574
+                $this->request,
2575
+                $this->shareManager,
2576
+                $this->groupManager,
2577
+                $this->userManager,
2578
+                $this->rootFolder,
2579
+                $this->urlGenerator,
2580
+                $this->l,
2581
+                $this->config,
2582
+                $this->appConfig,
2583
+                $this->appManager,
2584
+                $this->serverContainer,
2585
+                $this->userStatusManager,
2586
+                $this->previewManager,
2587
+                $this->dateTimeZone,
2588
+                $this->logger,
2589
+                $this->factory,
2590
+                $this->mailer,
2591
+                $this->tagManager,
2592
+                $this->getEmailValidatorWithStrictEmailCheck(),
2593
+                $this->trustedServers,
2594
+                $this->currentUser,
2595
+            ])->onlyMethods(['formatShare'])
2596
+            ->getMock();
2597
+
2598
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2599
+        $this->rootFolder->expects($this->exactly(2))
2600
+            ->method('getUserFolder')
2601
+            ->with('currentUser')
2602
+            ->willReturn($userFolder);
2603
+
2604
+        $userFolder->expects($this->once())
2605
+            ->method('get')
2606
+            ->with('valid-path')
2607
+            ->willReturn($path);
2608
+        $userFolder->method('getById')
2609
+            ->willReturn([]);
2610
+
2611
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2612
+
2613
+        $path->expects($this->once())
2614
+            ->method('lock')
2615
+            ->with(ILockingProvider::LOCK_SHARED);
2616
+
2617
+        $this->shareManager->method('createShare')
2618
+            ->with($this->callback(function (IShare $share) use ($path) {
2619
+                return $share->getNode() === $path
2620
+                    && $share->getPermissions() === (
2621
+                        Constants::PERMISSION_ALL
2622
+                        & ~Constants::PERMISSION_DELETE
2623
+                        & ~Constants::PERMISSION_CREATE
2624
+                    )
2625
+                    && $share->getShareType() === IShare::TYPE_REMOTE_GROUP
2626
+                    && $share->getSharedWith() === '[email protected]'
2627
+                    && $share->getSharedBy() === 'currentUser';
2628
+            }))
2629
+            ->willReturnArgument(0);
2630
+
2631
+        $this->shareManager->method('outgoingServer2ServerGroupSharesAllowed')->willReturn(true);
2632
+
2633
+        $expected = new DataResponse([]);
2634
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_REMOTE_GROUP, '[email protected]');
2635
+
2636
+        $this->assertInstanceOf(get_class($expected), $result);
2637
+        $this->assertEquals($expected->getData(), $result->getData());
2638
+    }
2639
+
2640
+    public function testCreateShareRoom(): void {
2641
+        $ocs = $this->mockFormatShare();
2642
+
2643
+        $share = $this->newShare();
2644
+        $this->shareManager->method('newShare')->willReturn($share);
2645
+
2646
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2647
+        $this->rootFolder->expects($this->exactly(2))
2648
+            ->method('getUserFolder')
2649
+            ->with('currentUser')
2650
+            ->willReturn($userFolder);
2651
+
2652
+        $userFolder->expects($this->once())
2653
+            ->method('get')
2654
+            ->with('valid-path')
2655
+            ->willReturn($path);
2656
+        $userFolder->method('getById')
2657
+            ->willReturn([]);
2658
+
2659
+        $path->expects($this->once())
2660
+            ->method('lock')
2661
+            ->with(ILockingProvider::LOCK_SHARED);
2662
+
2663
+        $this->appManager->method('isEnabledForUser')
2664
+            ->with('spreed')
2665
+            ->willReturn(true);
2666
+
2667
+        // This is not possible anymore with PHPUnit 10+
2668
+        // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2669
+        // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2670
+        $helper = $this->getMockBuilder(\stdClass::class)
2671
+            ->addMethods(['createShare'])
2672
+            ->getMock();
2673
+        $helper->method('createShare')
2674
+            ->with(
2675
+                $share,
2676
+                'recipientRoom',
2677
+                Constants::PERMISSION_ALL
2678
+                & ~Constants::PERMISSION_DELETE
2679
+                & ~Constants::PERMISSION_CREATE,
2680
+                ''
2681
+            )->willReturnCallback(
2682
+                function ($share): void {
2683
+                    $share->setSharedWith('recipientRoom');
2684
+                    $share->setPermissions(Constants::PERMISSION_ALL);
2685
+                }
2686
+            );
2687
+
2688
+        $this->serverContainer->method('get')
2689
+            ->with('\OCA\Talk\Share\Helper\ShareAPIController')
2690
+            ->willReturn($helper);
2691
+
2692
+        $this->shareManager->method('createShare')
2693
+            ->with($this->callback(function (IShare $share) use ($path) {
2694
+                return $share->getNode() === $path
2695
+                    && $share->getPermissions() === Constants::PERMISSION_ALL
2696
+                    && $share->getShareType() === IShare::TYPE_ROOM
2697
+                    && $share->getSharedWith() === 'recipientRoom'
2698
+                    && $share->getSharedBy() === 'currentUser';
2699
+            }))
2700
+            ->willReturnArgument(0);
2701
+
2702
+        $expected = new DataResponse([]);
2703
+        $result = $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2704
+
2705
+        $this->assertInstanceOf(get_class($expected), $result);
2706
+        $this->assertEquals($expected->getData(), $result->getData());
2707
+    }
2708
+
2709
+
2710
+    public function testCreateShareRoomHelperNotAvailable(): void {
2711
+        $this->expectException(OCSForbiddenException::class);
2712
+        $this->expectExceptionMessage('Sharing valid-path failed because the back end does not support room shares');
2713
+
2714
+        $ocs = $this->mockFormatShare();
2715
+
2716
+        $share = $this->newShare();
2717
+        $this->shareManager->method('newShare')->willReturn($share);
2718
+
2719
+        [$userFolder, $path] = $this->getNonSharedUserFolder();
2720
+        $this->rootFolder->method('getUserFolder')
2721
+            ->with('currentUser')
2722
+            ->willReturn($userFolder);
2723
+
2724
+        $path->method('getPath')->willReturn('valid-path');
2725
+        $userFolder->expects($this->once())
2726
+            ->method('get')
2727
+            ->with('valid-path')
2728
+            ->willReturn($path);
2729
+        $userFolder->method('getById')
2730
+            ->willReturn([]);
2731
+
2732
+        $path->expects($this->once())
2733
+            ->method('lock')
2734
+            ->with(ILockingProvider::LOCK_SHARED);
2735
+
2736
+        $this->appManager->method('isEnabledForUser')
2737
+            ->with('spreed')
2738
+            ->willReturn(false);
2739
+
2740
+        $this->shareManager->expects($this->never())->method('createShare');
2741
+
2742
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2743
+    }
2744
+
2745
+
2746
+    public function testCreateShareRoomHelperThrowException(): void {
2747
+        $this->expectException(OCSNotFoundException::class);
2748
+        $this->expectExceptionMessage('Exception thrown by the helper');
2749
+
2750
+        $ocs = $this->mockFormatShare();
2751
+
2752
+        $share = $this->newShare();
2753
+        $share->setSharedBy('currentUser');
2754
+        $this->shareManager->method('newShare')->willReturn($share);
2755
+
2756
+        [$userFolder, $path] = $this->getNonSharedUserFile();
2757
+        $this->rootFolder->method('getUserFolder')
2758
+            ->with('currentUser')
2759
+            ->willReturn($userFolder);
2760
+
2761
+        $userFolder->expects($this->once())
2762
+            ->method('get')
2763
+            ->with('valid-path')
2764
+            ->willReturn($path);
2765
+        $userFolder->method('getById')
2766
+            ->willReturn([]);
2767
+
2768
+        $path->expects($this->once())
2769
+            ->method('lock')
2770
+            ->with(ILockingProvider::LOCK_SHARED);
2771
+
2772
+        $this->appManager->method('isEnabledForUser')
2773
+            ->with('spreed')
2774
+            ->willReturn(true);
2775
+
2776
+        // This is not possible anymore with PHPUnit 10+
2777
+        // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
2778
+        // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
2779
+        $helper = $this->getMockBuilder(\stdClass::class)
2780
+            ->addMethods(['createShare'])
2781
+            ->getMock();
2782
+        $helper->method('createShare')
2783
+            ->with(
2784
+                $share,
2785
+                'recipientRoom',
2786
+                Constants::PERMISSION_ALL & ~(Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE),
2787
+                ''
2788
+            )->willReturnCallback(
2789
+                function ($share): void {
2790
+                    throw new OCSNotFoundException('Exception thrown by the helper');
2791
+                }
2792
+            );
2793
+
2794
+        $this->serverContainer->method('get')
2795
+            ->with('\OCA\Talk\Share\Helper\ShareAPIController')
2796
+            ->willReturn($helper);
2797
+
2798
+        $this->shareManager->expects($this->never())->method('createShare');
2799
+
2800
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_ROOM, 'recipientRoom');
2801
+    }
2802
+
2803
+    /**
2804
+     * Test for https://github.com/owncloud/core/issues/22587
2805
+     * TODO: Remove once proper solution is in place
2806
+     */
2807
+    public function testCreateReshareOfFederatedMountNoDeletePermissions(): void {
2808
+        $share = Server::get(IManager::class)->newShare();
2809
+        $this->shareManager->method('newShare')->willReturn($share);
2810
+
2811
+        /** @var ShareAPIController&MockObject $ocs */
2812
+        $ocs = $this->getMockBuilder(ShareAPIController::class)
2813
+            ->setConstructorArgs([
2814
+                $this->appName,
2815
+                $this->request,
2816
+                $this->shareManager,
2817
+                $this->groupManager,
2818
+                $this->userManager,
2819
+                $this->rootFolder,
2820
+                $this->urlGenerator,
2821
+                $this->l,
2822
+                $this->config,
2823
+                $this->appConfig,
2824
+                $this->appManager,
2825
+                $this->serverContainer,
2826
+                $this->userStatusManager,
2827
+                $this->previewManager,
2828
+                $this->dateTimeZone,
2829
+                $this->logger,
2830
+                $this->factory,
2831
+                $this->mailer,
2832
+                $this->tagManager,
2833
+                $this->getEmailValidatorWithStrictEmailCheck(),
2834
+                $this->trustedServers,
2835
+                $this->currentUser,
2836
+            ])->onlyMethods(['formatShare'])
2837
+            ->getMock();
2838
+
2839
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
2840
+        $this->rootFolder->expects($this->exactly(2))
2841
+            ->method('getUserFolder')
2842
+            ->with('currentUser')
2843
+            ->willReturn($userFolder);
2844
+
2845
+        $path = $this->getMockBuilder(Folder::class)->getMock();
2846
+        $path->method('getId')->willReturn(42);
2847
+
2848
+        $storage = $this->createMock(IStorage::class);
2849
+        $storage->method('instanceOfStorage')
2850
+            ->willReturnMap([
2851
+                ['OCA\Files_Sharing\External\Storage', true],
2852
+                ['OCA\Files_Sharing\SharedStorage', false],
2853
+            ]);
2854
+        $userFolder->method('getStorage')->willReturn($storage);
2855
+        $path->method('getStorage')->willReturn($storage);
2856
+
2857
+        $path->method('getPermissions')->willReturn(Constants::PERMISSION_READ);
2858
+        $userFolder->expects($this->once())
2859
+            ->method('get')
2860
+            ->with('valid-path')
2861
+            ->willReturn($path);
2862
+        $userFolder->method('getById')
2863
+            ->willReturn([]);
2864
+
2865
+        $this->userManager->method('userExists')->with('validUser')->willReturn(true);
2866
+
2867
+        $this->shareManager
2868
+            ->expects($this->once())
2869
+            ->method('createShare')
2870
+            ->with($this->callback(function (IShare $share) {
2871
+                return $share->getPermissions() === Constants::PERMISSION_READ;
2872
+            }))
2873
+            ->willReturnArgument(0);
2874
+
2875
+        $ocs->createShare('valid-path', Constants::PERMISSION_ALL, IShare::TYPE_USER, 'validUser');
2876
+    }
2877
+
2878
+
2879
+    public function testUpdateShareCantAccess(): void {
2880
+        $this->expectException(OCSNotFoundException::class);
2881
+        $this->expectExceptionMessage('Wrong share ID, share does not exist');
2882
+
2883
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
2884
+        $share = $this->newShare();
2885
+        $share->setNode($node);
2886
+
2887
+        $node->expects($this->once())
2888
+            ->method('lock')
2889
+            ->with(ILockingProvider::LOCK_SHARED);
2890
+
2891
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2892
+
2893
+        $this->rootFolder->method('getUserFolder')
2894
+            ->with($this->currentUser)
2895
+            ->willReturn($userFolder);
2896
+
2897
+        $userFolder->method('getById')
2898
+            ->with($share->getNodeId())
2899
+            ->willReturn([$share->getNode()]);
2900
+
2901
+        $this->ocs->updateShare(42);
2902
+    }
2903
+
2904
+
2905
+    public function testUpdateNoParametersLink(): void {
2906
+        $this->expectException(OCSBadRequestException::class);
2907
+        $this->expectExceptionMessage('Wrong or no update parameter given');
2908
+
2909
+        $node = $this->getMockBuilder(Folder::class)->getMock();
2910
+        $share = $this->newShare();
2911
+        $share->setPermissions(Constants::PERMISSION_ALL)
2912
+            ->setSharedBy($this->currentUser)
2913
+            ->setShareType(IShare::TYPE_LINK)
2914
+            ->setNode($node);
2915
+
2916
+        $node->expects($this->once())
2917
+            ->method('lock')
2918
+            ->with(ILockingProvider::LOCK_SHARED);
2919
+
2920
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2921
+
2922
+        $this->ocs->updateShare(42);
2923
+    }
2924
+
2925
+
2926
+    public function testUpdateNoParametersOther(): void {
2927
+        $this->expectException(OCSBadRequestException::class);
2928
+        $this->expectExceptionMessage('Wrong or no update parameter given');
2929
+
2930
+        $node = $this->getMockBuilder(Folder::class)->getMock();
2931
+        $share = $this->newShare();
2932
+        $share->setPermissions(Constants::PERMISSION_ALL)
2933
+            ->setSharedBy($this->currentUser)
2934
+            ->setShareType(IShare::TYPE_GROUP)
2935
+            ->setNode($node);
2936
+
2937
+        $node->expects($this->once())
2938
+            ->method('lock')
2939
+            ->with(ILockingProvider::LOCK_SHARED);
2940
+
2941
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2942
+
2943
+        $this->ocs->updateShare(42);
2944
+    }
2945
+
2946
+    public function testUpdateLinkShareClear(): void {
2947
+        $ocs = $this->mockFormatShare();
2948
+
2949
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
2950
+        $node->method('getId')
2951
+            ->willReturn(42);
2952
+        $share = $this->newShare();
2953
+        $share->setPermissions(Constants::PERMISSION_ALL)
2954
+            ->setSharedBy($this->currentUser)
2955
+            ->setShareType(IShare::TYPE_LINK)
2956
+            ->setPassword('password')
2957
+            ->setExpirationDate(new \DateTime())
2958
+            ->setNote('note')
2959
+            ->setLabel('label')
2960
+            ->setHideDownload(true)
2961
+            ->setPermissions(Constants::PERMISSION_ALL)
2962
+            ->setNode($node);
2963
+
2964
+        $node->expects($this->once())
2965
+            ->method('lock')
2966
+            ->with(ILockingProvider::LOCK_SHARED);
2967
+
2968
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
2969
+
2970
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
2971
+            $this->callback(function (IShare $share) {
2972
+                return $share->getPermissions() === Constants::PERMISSION_READ
2973
+                && $share->getPassword() === null
2974
+                && $share->getExpirationDate() === null
2975
+                // Once set a note or a label are never back to null, only to an
2976
+                // empty string.
2977
+                && $share->getNote() === ''
2978
+                && $share->getLabel() === ''
2979
+                && $share->getHideDownload() === false;
2980
+            })
2981
+        )->willReturnArgument(0);
2982
+
2983
+        $this->shareManager->method('getSharedWith')
2984
+            ->willReturn([]);
2985
+
2986
+        $this->rootFolder->method('getUserFolder')
2987
+            ->with($this->currentUser)
2988
+            ->willReturn($userFolder);
2989
+
2990
+        $userFolder->method('getById')
2991
+            ->with(42)
2992
+            ->willReturn([$node]);
2993
+        $userFolder->method('getFirstNodeById')
2994
+            ->with(42)
2995
+            ->willReturn($node);
2996
+
2997
+        $mountPoint = $this->createMock(IMountPoint::class);
2998
+        $node->method('getMountPoint')
2999
+            ->willReturn($mountPoint);
3000
+        $mountPoint->method('getStorageRootId')
3001
+            ->willReturn(42);
3002
+
3003
+        $expected = new DataResponse([]);
3004
+        $result = $ocs->updateShare(42, null, '', null, 'false', '', '', '', 'false');
3005
+
3006
+        $this->assertInstanceOf(get_class($expected), $result);
3007
+        $this->assertEquals($expected->getData(), $result->getData());
3008
+    }
3009
+
3010
+    public function testUpdateLinkShareSet(): void {
3011
+        $ocs = $this->mockFormatShare();
3012
+
3013
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3014
+        $folder->method('getId')
3015
+            ->willReturn(42);
3016
+
3017
+        $share = Server::get(IManager::class)->newShare();
3018
+        $share->setPermissions(Constants::PERMISSION_ALL)
3019
+            ->setSharedBy($this->currentUser)
3020
+            ->setShareType(IShare::TYPE_LINK)
3021
+            ->setNode($folder);
3022
+
3023
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3024
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3025
+
3026
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3027
+            $this->callback(function (IShare $share) {
3028
+                $date = new \DateTime('2000-01-01');
3029
+                $date->setTime(0, 0, 0);
3030
+
3031
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3032
+                && $share->getPassword() === 'password'
3033
+                && $share->getExpirationDate() == $date
3034
+                && $share->getNote() === 'note'
3035
+                && $share->getLabel() === 'label'
3036
+                && $share->getHideDownload() === true;
3037
+            })
3038
+        )->willReturnArgument(0);
3039
+
3040
+        $this->shareManager->method('getSharedWith')
3041
+            ->willReturn([]);
3042
+
3043
+        $this->rootFolder->method('getUserFolder')
3044
+            ->with($this->currentUser)
3045
+            ->willReturn($userFolder);
3046
+
3047
+        $userFolder->method('getById')
3048
+            ->with(42)
3049
+            ->willReturn([$folder]);
3050
+
3051
+        $mountPoint = $this->createMock(IMountPoint::class);
3052
+        $folder->method('getMountPoint')
3053
+            ->willReturn($mountPoint);
3054
+        $mountPoint->method('getStorageRootId')
3055
+            ->willReturn(42);
3056
+
3057
+        $expected = new DataResponse([]);
3058
+        $result = $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-01', 'note', 'label', 'true');
3059
+
3060
+        $this->assertInstanceOf(get_class($expected), $result);
3061
+        $this->assertEquals($expected->getData(), $result->getData());
3062
+    }
3063
+
3064
+    #[DataProvider('publicUploadParamsProvider')]
3065
+    public function testUpdateLinkShareEnablePublicUpload($permissions, $publicUpload, $expireDate, $password): void {
3066
+        $ocs = $this->mockFormatShare();
3067
+
3068
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3069
+        $folder->method('getId')
3070
+            ->willReturn(42);
3071
+
3072
+        $share = Server::get(IManager::class)->newShare();
3073
+        $share->setPermissions(Constants::PERMISSION_ALL)
3074
+            ->setSharedBy($this->currentUser)
3075
+            ->setShareType(IShare::TYPE_LINK)
3076
+            ->setPassword('password')
3077
+            ->setNode($folder);
3078
+
3079
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3080
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3081
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3082
+
3083
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3084
+            $this->callback(function (IShare $share) {
3085
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3086
+                && $share->getPassword() === 'password'
3087
+                && $share->getExpirationDate() === null;
3088
+            })
3089
+        )->willReturnArgument(0);
3090
+
3091
+        $this->rootFolder->method('getUserFolder')
3092
+            ->with($this->currentUser)
3093
+            ->willReturn($userFolder);
3094
+
3095
+        $userFolder->method('getById')
3096
+            ->with(42)
3097
+            ->willReturn([$folder]);
3098
+
3099
+        $mountPoint = $this->createMock(IMountPoint::class);
3100
+        $folder->method('getMountPoint')
3101
+            ->willReturn($mountPoint);
3102
+        $mountPoint->method('getStorageRootId')
3103
+            ->willReturn(42);
3104
+
3105
+        $expected = new DataResponse([]);
3106
+        $result = $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3107
+
3108
+        $this->assertInstanceOf(get_class($expected), $result);
3109
+        $this->assertEquals($expected->getData(), $result->getData());
3110
+    }
3111
+
3112
+
3113
+    public static function publicLinkValidPermissionsProvider() {
3114
+        return [
3115
+            [Constants::PERMISSION_CREATE],
3116
+            [Constants::PERMISSION_READ],
3117
+            [Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE],
3118
+            [Constants::PERMISSION_READ | Constants::PERMISSION_DELETE],
3119
+            [Constants::PERMISSION_READ | Constants::PERMISSION_CREATE],
3120
+        ];
3121
+    }
3122
+
3123
+    #[DataProvider('publicLinkValidPermissionsProvider')]
3124
+    public function testUpdateLinkShareSetCRUDPermissions($permissions): void {
3125
+        $ocs = $this->mockFormatShare();
3126
+
3127
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3128
+        $folder->method('getId')
3129
+            ->willReturn(42);
3130
+
3131
+        $share = Server::get(IManager::class)->newShare();
3132
+        $share->setPermissions(Constants::PERMISSION_ALL)
3133
+            ->setSharedBy($this->currentUser)
3134
+            ->setShareType(IShare::TYPE_LINK)
3135
+            ->setPassword('password')
3136
+            ->setNode($folder);
3137
+
3138
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3139
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3140
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3141
+
3142
+        $this->shareManager
3143
+            ->expects($this->any())
3144
+            ->method('updateShare')
3145
+            ->willReturnArgument(0);
3146
+
3147
+        $this->rootFolder->method('getUserFolder')
3148
+            ->with($this->currentUser)
3149
+            ->willReturn($userFolder);
3150
+
3151
+        $userFolder->method('getById')
3152
+            ->with(42)
3153
+            ->willReturn([$folder]);
3154
+
3155
+        $mountPoint = $this->createMock(IMountPoint::class);
3156
+        $folder->method('getMountPoint')
3157
+            ->willReturn($mountPoint);
3158
+        $mountPoint->method('getStorageRootId')
3159
+            ->willReturn(42);
3160
+
3161
+        $expected = new DataResponse([]);
3162
+        $result = $ocs->updateShare(42, $permissions, 'password', null, null, null);
3163
+
3164
+        $this->assertInstanceOf(get_class($expected), $result);
3165
+        $this->assertEquals($expected->getData(), $result->getData());
3166
+    }
3167
+
3168
+    public static function publicLinkInvalidPermissionsProvider1() {
3169
+        return [
3170
+            [Constants::PERMISSION_DELETE],
3171
+            [Constants::PERMISSION_UPDATE],
3172
+            [Constants::PERMISSION_SHARE],
3173
+        ];
3174
+    }
3175
+
3176
+    #[DataProvider('publicLinkInvalidPermissionsProvider1')]
3177
+    public function testUpdateLinkShareSetInvalidCRUDPermissions1($permissions): void {
3178
+        $this->expectException(OCSBadRequestException::class);
3179
+        $this->expectExceptionMessage('Share must at least have READ or CREATE permissions');
3180
+
3181
+        $this->testUpdateLinkShareSetCRUDPermissions($permissions, null);
3182
+    }
3183
+
3184
+    public static function publicLinkInvalidPermissionsProvider2() {
3185
+        return [
3186
+            [Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE],
3187
+            [Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE],
3188
+        ];
3189
+    }
3190
+
3191
+    #[DataProvider('publicLinkInvalidPermissionsProvider2')]
3192
+    public function testUpdateLinkShareSetInvalidCRUDPermissions2($permissions): void {
3193
+        $this->expectException(OCSBadRequestException::class);
3194
+        $this->expectExceptionMessage('Share must have READ permission if UPDATE or DELETE permission is set');
3195
+
3196
+        $this->testUpdateLinkShareSetCRUDPermissions($permissions);
3197
+    }
3198
+
3199
+    public function testUpdateLinkShareInvalidDate(): void {
3200
+        $this->expectException(OCSBadRequestException::class);
3201
+        $this->expectExceptionMessage('Invalid date. Format must be YYYY-MM-DD');
3202
+
3203
+        $ocs = $this->mockFormatShare();
3204
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3205
+        $userFolder->method('getById')
3206
+            ->with(42)
3207
+            ->willReturn([$folder]);
3208
+        $this->rootFolder->method('getUserFolder')
3209
+            ->with($this->currentUser)
3210
+            ->willReturn($userFolder);
3211
+
3212
+        $folder->method('getId')
3213
+            ->willReturn(42);
3214
+
3215
+        $share = Server::get(IManager::class)->newShare();
3216
+        $share->setPermissions(Constants::PERMISSION_ALL)
3217
+            ->setSharedBy($this->currentUser)
3218
+            ->setShareType(IShare::TYPE_LINK)
3219
+            ->setNode($folder);
3220
+
3221
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3222
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3223
+
3224
+        $ocs->updateShare(42, null, 'password', null, 'true', '2000-01-a');
3225
+    }
3226
+
3227
+    public static function publicUploadParamsProvider() {
3228
+        return [
3229
+            [null, 'true', null, 'password'],
3230
+            // legacy had no delete
3231
+            [
3232
+                Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE,
3233
+                'true', null, 'password'
3234
+            ],
3235
+            // correct
3236
+            [
3237
+                Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE,
3238
+                null, null, 'password'
3239
+            ],
3240
+        ];
3241
+    }
3242
+
3243
+    #[DataProvider('publicUploadParamsProvider')]
3244
+    public function testUpdateLinkSharePublicUploadNotAllowed($permissions, $publicUpload, $expireDate, $password): void {
3245
+        $this->expectException(OCSForbiddenException::class);
3246
+        $this->expectExceptionMessage('Public upload disabled by the administrator');
3247
+
3248
+        $ocs = $this->mockFormatShare();
3249
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3250
+        $userFolder->method('getById')
3251
+            ->with(42)
3252
+            ->willReturn([$folder]);
3253
+        $this->rootFolder->method('getUserFolder')
3254
+            ->with($this->currentUser)
3255
+            ->willReturn($userFolder);
3256
+
3257
+        $folder->method('getId')->willReturn(42);
3258
+
3259
+        $share = Server::get(IManager::class)->newShare();
3260
+        $share->setPermissions(Constants::PERMISSION_ALL)
3261
+            ->setSharedBy($this->currentUser)
3262
+            ->setShareType(IShare::TYPE_LINK)
3263
+            ->setNode($folder);
3264
+
3265
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3266
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(false);
3267
+
3268
+        $ocs->updateShare(42, $permissions, $password, null, $publicUpload, $expireDate);
3269
+    }
3270
+
3271
+
3272
+    public function testUpdateLinkSharePublicUploadOnFile(): void {
3273
+        $this->expectException(OCSBadRequestException::class);
3274
+        $this->expectExceptionMessage('Public upload is only possible for publicly shared folders');
3275
+
3276
+        $ocs = $this->mockFormatShare();
3277
+
3278
+        $file = $this->getMockBuilder(File::class)->getMock();
3279
+        $file->method('getId')
3280
+            ->willReturn(42);
3281
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3282
+        $userFolder->method('getById')
3283
+            ->with(42)
3284
+            ->willReturn([$folder]);
3285
+        $this->rootFolder->method('getUserFolder')
3286
+            ->with($this->currentUser)
3287
+            ->willReturn($userFolder);
3288
+
3289
+        $share = Server::get(IManager::class)->newShare();
3290
+        $share->setPermissions(Constants::PERMISSION_ALL)
3291
+            ->setSharedBy($this->currentUser)
3292
+            ->setShareType(IShare::TYPE_LINK)
3293
+            ->setNode($file);
3294
+
3295
+        $this->shareManager
3296
+            ->method('getShareById')
3297
+            ->with('ocinternal:42')
3298
+            ->willReturn($share);
3299
+        $this->shareManager
3300
+            ->method('shareApiLinkAllowPublicUpload')
3301
+            ->willReturn(true);
3302
+        $this->shareManager
3303
+            ->method('updateShare')
3304
+            ->with($share)
3305
+            ->willThrowException(new \InvalidArgumentException('File shares cannot have create or delete permissions'));
3306
+
3307
+        $ocs->updateShare(42, null, 'password', null, 'true', '');
3308
+    }
3309
+
3310
+    public function testUpdateLinkSharePasswordDoesNotChangeOther(): void {
3311
+        $ocs = $this->mockFormatShare();
3312
+
3313
+        $date = new \DateTime('2000-01-01');
3314
+        $date->setTime(0, 0, 0);
3315
+
3316
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3317
+        $node->method('getId')->willReturn(42);
3318
+        $userFolder->method('getById')
3319
+            ->with(42)
3320
+            ->willReturn([$node]);
3321
+        $this->rootFolder->method('getUserFolder')
3322
+            ->with($this->currentUser)
3323
+            ->willReturn($userFolder);
3324
+        $share = $this->newShare();
3325
+        $share->setPermissions(Constants::PERMISSION_ALL)
3326
+            ->setSharedBy($this->currentUser)
3327
+            ->setShareType(IShare::TYPE_LINK)
3328
+            ->setPassword('password')
3329
+            ->setSendPasswordByTalk(true)
3330
+            ->setExpirationDate($date)
3331
+            ->setNote('note')
3332
+            ->setLabel('label')
3333
+            ->setHideDownload(true)
3334
+            ->setPermissions(Constants::PERMISSION_ALL)
3335
+            ->setNode($node);
3336
+
3337
+        $node->expects($this->once())
3338
+            ->method('lock')
3339
+            ->with(ILockingProvider::LOCK_SHARED);
3340
+
3341
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3342
+
3343
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3344
+            $this->callback(function (IShare $share) use ($date) {
3345
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3346
+                && $share->getPassword() === 'newpassword'
3347
+                && $share->getSendPasswordByTalk() === true
3348
+                && $share->getExpirationDate() === $date
3349
+                && $share->getNote() === 'note'
3350
+                && $share->getLabel() === 'label'
3351
+                && $share->getHideDownload() === true;
3352
+            })
3353
+        )->willReturnArgument(0);
3354
+
3355
+        $expected = new DataResponse([]);
3356
+        $result = $ocs->updateShare(42, null, 'newpassword', null, null, null, null, null, null);
3357
+
3358
+        $this->assertInstanceOf(get_class($expected), $result);
3359
+        $this->assertEquals($expected->getData(), $result->getData());
3360
+    }
3361
+
3362
+    public function testUpdateLinkShareSendPasswordByTalkDoesNotChangeOther(): void {
3363
+        $ocs = $this->mockFormatShare();
3364
+
3365
+        $date = new \DateTime('2000-01-01');
3366
+        $date->setTime(0, 0, 0);
3367
+
3368
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3369
+        $userFolder->method('getById')
3370
+            ->with(42)
3371
+            ->willReturn([$node]);
3372
+        $this->rootFolder->method('getUserFolder')
3373
+            ->with($this->currentUser)
3374
+            ->willReturn($userFolder);
3375
+        $node->method('getId')->willReturn(42);
3376
+        $share = $this->newShare();
3377
+        $share->setPermissions(Constants::PERMISSION_ALL)
3378
+            ->setSharedBy($this->currentUser)
3379
+            ->setShareType(IShare::TYPE_LINK)
3380
+            ->setPassword('password')
3381
+            ->setSendPasswordByTalk(false)
3382
+            ->setExpirationDate($date)
3383
+            ->setNote('note')
3384
+            ->setLabel('label')
3385
+            ->setHideDownload(true)
3386
+            ->setPermissions(Constants::PERMISSION_ALL)
3387
+            ->setNode($node);
3388
+
3389
+        $node->expects($this->once())
3390
+            ->method('lock')
3391
+            ->with(ILockingProvider::LOCK_SHARED);
3392
+
3393
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3394
+
3395
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3396
+
3397
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3398
+            $this->callback(function (IShare $share) use ($date) {
3399
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3400
+                && $share->getPassword() === 'password'
3401
+                && $share->getSendPasswordByTalk() === true
3402
+                && $share->getExpirationDate() === $date
3403
+                && $share->getNote() === 'note'
3404
+                && $share->getLabel() === 'label'
3405
+                && $share->getHideDownload() === true;
3406
+            })
3407
+        )->willReturnArgument(0);
3408
+
3409
+        $expected = new DataResponse([]);
3410
+        $result = $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3411
+
3412
+        $this->assertInstanceOf(get_class($expected), $result);
3413
+        $this->assertEquals($expected->getData(), $result->getData());
3414
+    }
3415
+
3416
+
3417
+    public function testUpdateLinkShareSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3418
+        $this->expectException(OCSForbiddenException::class);
3419
+        $this->expectExceptionMessage('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.');
3420
+
3421
+        $ocs = $this->mockFormatShare();
3422
+
3423
+        $date = new \DateTime('2000-01-01');
3424
+        $date->setTime(0, 0, 0);
3425
+
3426
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3427
+        $userFolder->method('getById')
3428
+            ->with(42)
3429
+            ->willReturn([$node]);
3430
+        $this->rootFolder->method('getUserFolder')
3431
+            ->with($this->currentUser)
3432
+            ->willReturn($userFolder);
3433
+        $node->method('getId')->willReturn(42);
3434
+        $share = $this->newShare();
3435
+        $share->setPermissions(Constants::PERMISSION_ALL)
3436
+            ->setSharedBy($this->currentUser)
3437
+            ->setShareType(IShare::TYPE_LINK)
3438
+            ->setPassword('password')
3439
+            ->setSendPasswordByTalk(false)
3440
+            ->setExpirationDate($date)
3441
+            ->setNote('note')
3442
+            ->setLabel('label')
3443
+            ->setHideDownload(true)
3444
+            ->setPermissions(Constants::PERMISSION_ALL)
3445
+            ->setNode($node);
3446
+
3447
+        $node->expects($this->once())
3448
+            ->method('lock')
3449
+            ->with(ILockingProvider::LOCK_SHARED);
3450
+
3451
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3452
+
3453
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3454
+
3455
+        $this->shareManager->expects($this->never())->method('updateShare');
3456
+
3457
+        $ocs->updateShare(42, null, null, 'true', null, null, null, null, null);
3458
+    }
3459
+
3460
+    public function testUpdateLinkShareDoNotSendPasswordByTalkDoesNotChangeOther(): void {
3461
+        $ocs = $this->mockFormatShare();
3462
+
3463
+        $date = new \DateTime('2000-01-01');
3464
+        $date->setTime(0, 0, 0);
3465
+
3466
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3467
+        $userFolder->method('getById')
3468
+            ->with(42)
3469
+            ->willReturn([$node]);
3470
+        $this->rootFolder->method('getUserFolder')
3471
+            ->with($this->currentUser)
3472
+            ->willReturn($userFolder);
3473
+        $node->method('getId')->willReturn(42);
3474
+        $share = $this->newShare();
3475
+        $share->setPermissions(Constants::PERMISSION_ALL)
3476
+            ->setSharedBy($this->currentUser)
3477
+            ->setShareType(IShare::TYPE_LINK)
3478
+            ->setPassword('password')
3479
+            ->setSendPasswordByTalk(true)
3480
+            ->setExpirationDate($date)
3481
+            ->setNote('note')
3482
+            ->setLabel('label')
3483
+            ->setHideDownload(true)
3484
+            ->setPermissions(Constants::PERMISSION_ALL)
3485
+            ->setNode($node);
3486
+
3487
+        $node->expects($this->once())
3488
+            ->method('lock')
3489
+            ->with(ILockingProvider::LOCK_SHARED);
3490
+
3491
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3492
+
3493
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(true);
3494
+
3495
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3496
+            $this->callback(function (IShare $share) use ($date) {
3497
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3498
+                && $share->getPassword() === 'password'
3499
+                && $share->getSendPasswordByTalk() === false
3500
+                && $share->getExpirationDate() === $date
3501
+                && $share->getNote() === 'note'
3502
+                && $share->getLabel() === 'label'
3503
+                && $share->getHideDownload() === true;
3504
+            })
3505
+        )->willReturnArgument(0);
3506
+
3507
+        $expected = new DataResponse([]);
3508
+        $result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3509
+
3510
+        $this->assertInstanceOf(get_class($expected), $result);
3511
+        $this->assertEquals($expected->getData(), $result->getData());
3512
+    }
3513
+
3514
+    public function testUpdateLinkShareDoNotSendPasswordByTalkWithTalkDisabledDoesNotChangeOther(): void {
3515
+        $ocs = $this->mockFormatShare();
3516
+
3517
+        $date = new \DateTime('2000-01-01');
3518
+        $date->setTime(0, 0, 0);
3519
+
3520
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3521
+        $node->method('getId')
3522
+            ->willReturn(42);
3523
+
3524
+        $share = $this->newShare();
3525
+        $share->setPermissions(Constants::PERMISSION_ALL)
3526
+            ->setSharedBy($this->currentUser)
3527
+            ->setShareType(IShare::TYPE_LINK)
3528
+            ->setPassword('password')
3529
+            ->setSendPasswordByTalk(true)
3530
+            ->setExpirationDate($date)
3531
+            ->setNote('note')
3532
+            ->setLabel('label')
3533
+            ->setHideDownload(true)
3534
+            ->setPermissions(Constants::PERMISSION_ALL)
3535
+            ->setNode($node);
3536
+
3537
+        $node->expects($this->once())
3538
+            ->method('lock')
3539
+            ->with(ILockingProvider::LOCK_SHARED);
3540
+
3541
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3542
+
3543
+        $this->appManager->method('isEnabledForUser')->with('spreed')->willReturn(false);
3544
+
3545
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3546
+            $this->callback(function (IShare $share) use ($date) {
3547
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3548
+                && $share->getPassword() === 'password'
3549
+                && $share->getSendPasswordByTalk() === false
3550
+                && $share->getExpirationDate() === $date
3551
+                && $share->getNote() === 'note'
3552
+                && $share->getLabel() === 'label'
3553
+                && $share->getHideDownload() === true;
3554
+            })
3555
+        )->willReturnArgument(0);
3556
+
3557
+        $this->rootFolder->method('getUserFolder')
3558
+            ->with($this->currentUser)
3559
+            ->willReturn($userFolder);
3560
+
3561
+        $userFolder->method('getById')
3562
+            ->with(42)
3563
+            ->willReturn([$node]);
3564
+
3565
+        $mountPoint = $this->createMock(IMountPoint::class);
3566
+        $node->method('getMountPoint')
3567
+            ->willReturn($mountPoint);
3568
+        $mountPoint->method('getStorageRootId')
3569
+            ->willReturn(42);
3570
+
3571
+        $mountPoint = $this->createMock(IMountPoint::class);
3572
+        $node->method('getMountPoint')
3573
+            ->willReturn($mountPoint);
3574
+        $mountPoint->method('getStorageRootId')
3575
+            ->willReturn(42);
3576
+
3577
+        $expected = new DataResponse([]);
3578
+        $result = $ocs->updateShare(42, null, null, 'false', null, null, null, null, null);
3579
+
3580
+        $this->assertInstanceOf(get_class($expected), $result);
3581
+        $this->assertEquals($expected->getData(), $result->getData());
3582
+    }
3583
+
3584
+    public function testUpdateLinkShareExpireDateDoesNotChangeOther(): void {
3585
+        $ocs = $this->mockFormatShare();
3586
+
3587
+        [$userFolder, $node] = $this->getNonSharedUserFolder();
3588
+        $node->method('getId')
3589
+            ->willReturn(42);
3590
+
3591
+        $share = $this->newShare();
3592
+        $share->setPermissions(Constants::PERMISSION_ALL)
3593
+            ->setSharedBy($this->currentUser)
3594
+            ->setShareType(IShare::TYPE_LINK)
3595
+            ->setPassword('password')
3596
+            ->setSendPasswordByTalk(true)
3597
+            ->setExpirationDate(new \DateTime())
3598
+            ->setNote('note')
3599
+            ->setLabel('label')
3600
+            ->setHideDownload(true)
3601
+            ->setPermissions(Constants::PERMISSION_ALL)
3602
+            ->setNode($node);
3603
+
3604
+        $node->expects($this->once())
3605
+            ->method('lock')
3606
+            ->with(ILockingProvider::LOCK_SHARED);
3607
+
3608
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3609
+
3610
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3611
+            $this->callback(function (IShare $share) {
3612
+                $date = new \DateTime('2010-12-23');
3613
+                $date->setTime(0, 0, 0);
3614
+
3615
+                return $share->getPermissions() === Constants::PERMISSION_ALL
3616
+                && $share->getPassword() === 'password'
3617
+                && $share->getSendPasswordByTalk() === true
3618
+                && $share->getExpirationDate() == $date
3619
+                && $share->getNote() === 'note'
3620
+                && $share->getLabel() === 'label'
3621
+                && $share->getHideDownload() === true;
3622
+            })
3623
+        )->willReturnArgument(0);
3624
+
3625
+        $this->rootFolder->method('getUserFolder')
3626
+            ->with($this->currentUser)
3627
+            ->willReturn($userFolder);
3628
+
3629
+        $userFolder->method('getById')
3630
+            ->with(42)
3631
+            ->willReturn([$node]);
3632
+
3633
+        $mountPoint = $this->createMock(IMountPoint::class);
3634
+        $node->method('getMountPoint')
3635
+            ->willReturn($mountPoint);
3636
+        $mountPoint->method('getStorageRootId')
3637
+            ->willReturn(42);
3638
+
3639
+        $expected = new DataResponse([]);
3640
+        $result = $ocs->updateShare(42, null, null, null, null, '2010-12-23', null, null, null);
3641
+
3642
+        $this->assertInstanceOf(get_class($expected), $result);
3643
+        $this->assertEquals($expected->getData(), $result->getData());
3644
+    }
3645
+
3646
+    public function testUpdateLinkSharePublicUploadDoesNotChangeOther(): void {
3647
+        $ocs = $this->mockFormatShare();
3648
+
3649
+        $date = new \DateTime('2000-01-01');
3650
+
3651
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3652
+        $folder->method('getId')
3653
+            ->willReturn(42);
3654
+
3655
+        $share = Server::get(IManager::class)->newShare();
3656
+        $share->setPermissions(Constants::PERMISSION_ALL)
3657
+            ->setSharedBy($this->currentUser)
3658
+            ->setShareType(IShare::TYPE_LINK)
3659
+            ->setPassword('password')
3660
+            ->setSendPasswordByTalk(true)
3661
+            ->setExpirationDate($date)
3662
+            ->setNote('note')
3663
+            ->setLabel('label')
3664
+            ->setHideDownload(true)
3665
+            ->setPermissions(Constants::PERMISSION_ALL)
3666
+            ->setNode($folder);
3667
+
3668
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3669
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3670
+
3671
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3672
+            $this->callback(function (IShare $share) use ($date) {
3673
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3674
+                && $share->getPassword() === 'password'
3675
+                && $share->getSendPasswordByTalk() === true
3676
+                && $share->getExpirationDate() === $date
3677
+                && $share->getNote() === 'note'
3678
+                && $share->getLabel() === 'label'
3679
+                && $share->getHideDownload() === true;
3680
+            })
3681
+        )->willReturnArgument(0);
3682
+
3683
+        $this->shareManager->method('getSharedWith')
3684
+            ->willReturn([]);
3685
+
3686
+        $this->rootFolder->method('getUserFolder')
3687
+            ->with($this->currentUser)
3688
+            ->willReturn($userFolder);
3689
+
3690
+        $userFolder->method('getById')
3691
+            ->with(42)
3692
+            ->willReturn([$folder]);
3693
+
3694
+        $mountPoint = $this->createMock(IMountPoint::class);
3695
+        $folder->method('getMountPoint')
3696
+            ->willReturn($mountPoint);
3697
+        $mountPoint->method('getStorageRootId')
3698
+            ->willReturn(42);
3699
+
3700
+        $expected = new DataResponse([]);
3701
+        $result = $ocs->updateShare(42, null, null, null, 'true', null, null, null, null);
3702
+
3703
+        $this->assertInstanceOf(get_class($expected), $result);
3704
+        $this->assertEquals($expected->getData(), $result->getData());
3705
+    }
3706
+
3707
+    public function testUpdateLinkSharePermissions(): void {
3708
+        $ocs = $this->mockFormatShare();
3709
+
3710
+        $date = new \DateTime('2000-01-01');
3711
+
3712
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3713
+        $folder->method('getId')
3714
+            ->willReturn(42);
3715
+
3716
+        $share = Server::get(IManager::class)->newShare();
3717
+        $share->setPermissions(Constants::PERMISSION_ALL)
3718
+            ->setSharedBy($this->currentUser)
3719
+            ->setShareType(IShare::TYPE_LINK)
3720
+            ->setPassword('password')
3721
+            ->setSendPasswordByTalk(true)
3722
+            ->setExpirationDate($date)
3723
+            ->setNote('note')
3724
+            ->setLabel('label')
3725
+            ->setHideDownload(true)
3726
+            ->setPermissions(Constants::PERMISSION_ALL)
3727
+            ->setNode($folder);
3728
+
3729
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3730
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3731
+
3732
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3733
+            $this->callback(function (IShare $share) use ($date): bool {
3734
+                return $share->getPermissions() === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
3735
+                && $share->getPassword() === 'password'
3736
+                && $share->getSendPasswordByTalk() === true
3737
+                && $share->getExpirationDate() === $date
3738
+                && $share->getNote() === 'note'
3739
+                && $share->getLabel() === 'label'
3740
+                && $share->getHideDownload() === true;
3741
+            })
3742
+        )->willReturnArgument(0);
3743
+
3744
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3745
+
3746
+        $this->rootFolder->method('getUserFolder')
3747
+            ->with($this->currentUser)
3748
+            ->willReturn($userFolder);
3749
+
3750
+        $userFolder->method('getById')
3751
+            ->with(42)
3752
+            ->willReturn([$folder]);
3753
+
3754
+        $mountPoint = $this->createMock(IMountPoint::class);
3755
+        $folder->method('getMountPoint')
3756
+            ->willReturn($mountPoint);
3757
+        $mountPoint->method('getStorageRootId')
3758
+            ->willReturn(42);
3759
+
3760
+        $expected = new DataResponse([]);
3761
+        $result = $ocs->updateShare(42, 7, null, null, 'true', null, null, null, null);
3762
+
3763
+        $this->assertInstanceOf(get_class($expected), $result);
3764
+        $this->assertEquals($expected->getData(), $result->getData());
3765
+    }
3766
+
3767
+    public function testUpdateLinkSharePermissionsShare(): void {
3768
+        $ocs = $this->mockFormatShare();
3769
+
3770
+        $date = new \DateTime('2000-01-01');
3771
+
3772
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3773
+        $folder->method('getId')
3774
+            ->willReturn(42);
3775
+
3776
+        $share = Server::get(IManager::class)->newShare();
3777
+        $share->setPermissions(Constants::PERMISSION_ALL)
3778
+            ->setSharedBy($this->currentUser)
3779
+            ->setShareType(IShare::TYPE_LINK)
3780
+            ->setPassword('password')
3781
+            ->setSendPasswordByTalk(true)
3782
+            ->setExpirationDate($date)
3783
+            ->setNote('note')
3784
+            ->setLabel('label')
3785
+            ->setHideDownload(true)
3786
+            ->setPermissions(Constants::PERMISSION_READ)
3787
+            ->setNode($folder);
3788
+
3789
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3790
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3791
+
3792
+        $this->shareManager->expects($this->once())
3793
+            ->method('updateShare')
3794
+            ->with(
3795
+                $this->callback(function (IShare $share) use ($date) {
3796
+                    return $share->getPermissions() === Constants::PERMISSION_ALL
3797
+                        && $share->getPassword() === 'password'
3798
+                        && $share->getSendPasswordByTalk() === true
3799
+                        && $share->getExpirationDate() === $date
3800
+                        && $share->getNote() === 'note'
3801
+                        && $share->getLabel() === 'label'
3802
+                        && $share->getHideDownload() === true;
3803
+                })
3804
+            )->willReturnArgument(0);
3805
+
3806
+        $this->rootFolder->method('getUserFolder')
3807
+            ->with($this->currentUser)
3808
+            ->willReturn($userFolder);
3809
+
3810
+        $userFolder->method('getById')
3811
+            ->with(42)
3812
+            ->willReturn([$folder]);
3813
+
3814
+        $mountPoint = $this->createMock(IMountPoint::class);
3815
+        $folder->method('getMountPoint')
3816
+            ->willReturn($mountPoint);
3817
+        $mountPoint->method('getStorageRootId')
3818
+            ->willReturn(42);
3819
+
3820
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3821
+
3822
+        $expected = new DataResponse([]);
3823
+        $result = $ocs->updateShare(42, Constants::PERMISSION_ALL, null, null, null, null, null, null, null);
3824
+
3825
+        $this->assertInstanceOf(get_class($expected), $result);
3826
+        $this->assertEquals($expected->getData(), $result->getData());
3827
+    }
3828
+
3829
+    public function testUpdateOtherPermissions(): void {
3830
+        $ocs = $this->mockFormatShare();
3831
+
3832
+        [$userFolder, $file] = $this->getNonSharedUserFolder();
3833
+        $file->method('getId')
3834
+            ->willReturn(42);
3835
+
3836
+        $share = Server::get(IManager::class)->newShare();
3837
+        $share->setPermissions(Constants::PERMISSION_ALL)
3838
+            ->setSharedBy($this->currentUser)
3839
+            ->setShareType(IShare::TYPE_USER)
3840
+            ->setNode($file);
3841
+
3842
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3843
+        $this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true);
3844
+
3845
+        $this->shareManager->expects($this->once())->method('updateShare')->with(
3846
+            $this->callback(function (IShare $share) {
3847
+                return $share->getPermissions() === Constants::PERMISSION_ALL;
3848
+            })
3849
+        )->willReturnArgument(0);
3850
+
3851
+        $this->shareManager->method('getSharedWith')->willReturn([]);
3852
+
3853
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3854
+        $this->rootFolder->method('getUserFolder')
3855
+            ->with($this->currentUser)
3856
+            ->willReturn($userFolder);
3857
+
3858
+        $userFolder->method('getById')
3859
+            ->with(42)
3860
+            ->willReturn([$file]);
3861
+
3862
+        $mountPoint = $this->createMock(IMountPoint::class);
3863
+        $file->method('getMountPoint')
3864
+            ->willReturn($mountPoint);
3865
+        $mountPoint->method('getStorageRootId')
3866
+            ->willReturn(42);
3867
+
3868
+        $expected = new DataResponse([]);
3869
+        $result = $ocs->updateShare(42, 31, null, null, null, null);
3870
+
3871
+        $this->assertInstanceOf(get_class($expected), $result);
3872
+        $this->assertEquals($expected->getData(), $result->getData());
3873
+    }
3874
+
3875
+    public function testUpdateShareCannotIncreasePermissions(): void {
3876
+        $ocs = $this->mockFormatShare();
3877
+
3878
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3879
+        $folder->method('getId')
3880
+            ->willReturn(42);
3881
+
3882
+        $share = Server::get(IManager::class)->newShare();
3883
+        $share
3884
+            ->setId(42)
3885
+            ->setSharedBy($this->currentUser)
3886
+            ->setShareOwner('anotheruser')
3887
+            ->setShareType(IShare::TYPE_GROUP)
3888
+            ->setSharedWith('group1')
3889
+            ->setPermissions(Constants::PERMISSION_READ)
3890
+            ->setNode($folder);
3891
+
3892
+        // note: updateShare will modify the received instance but getSharedWith will reread from the database,
3893
+        // so their values will be different
3894
+        $incomingShare = Server::get(IManager::class)->newShare();
3895
+        $incomingShare
3896
+            ->setId(42)
3897
+            ->setSharedBy($this->currentUser)
3898
+            ->setShareOwner('anotheruser')
3899
+            ->setShareType(IShare::TYPE_GROUP)
3900
+            ->setSharedWith('group1')
3901
+            ->setPermissions(Constants::PERMISSION_READ)
3902
+            ->setNode($folder);
3903
+
3904
+        $this->request
3905
+            ->method('getParam')
3906
+            ->willReturnMap([
3907
+                ['permissions', null, '31'],
3908
+            ]);
3909
+
3910
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3911
+
3912
+        $this->shareManager->expects($this->any())
3913
+            ->method('getSharedWith')
3914
+            ->willReturnMap([
3915
+                ['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3916
+                ['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]],
3917
+                ['currentUser', IShare::TYPE_ROOM, $share->getNode(), -1, 0, []]
3918
+            ]);
3919
+
3920
+        $this->rootFolder->method('getUserFolder')
3921
+            ->with($this->currentUser)
3922
+            ->willReturn($userFolder);
3923
+
3924
+        $userFolder->method('getById')
3925
+            ->with(42)
3926
+            ->willReturn([$folder]);
3927
+        $userFolder->method('getFirstNodeById')
3928
+            ->with(42)
3929
+            ->willReturn($folder);
3930
+
3931
+        $mountPoint = $this->createMock(IMountPoint::class);
3932
+        $folder->method('getMountPoint')
3933
+            ->willReturn($mountPoint);
3934
+        $mountPoint->method('getStorageRootId')
3935
+            ->willReturn(42);
3936
+
3937
+        $this->shareManager->expects($this->once())
3938
+            ->method('updateShare')
3939
+            ->with($share)
3940
+            ->willThrowException(new GenericShareException('Cannot increase permissions of path/file', 'Cannot increase permissions of path/file', 404));
3941
+
3942
+        try {
3943
+            $ocs->updateShare(42, 31);
3944
+            $this->fail();
3945
+        } catch (OCSException $e) {
3946
+            $this->assertEquals('Cannot increase permissions of path/file', $e->getMessage());
3947
+        }
3948
+    }
3949
+
3950
+    public function testUpdateShareCanIncreasePermissionsIfOwner(): void {
3951
+        $ocs = $this->mockFormatShare();
3952
+
3953
+        [$userFolder, $folder] = $this->getNonSharedUserFolder();
3954
+        $folder->method('getId')
3955
+            ->willReturn(42);
3956
+
3957
+        $share = Server::get(IManager::class)->newShare();
3958
+        $share
3959
+            ->setId(42)
3960
+            ->setSharedBy($this->currentUser)
3961
+            ->setShareOwner($this->currentUser)
3962
+            ->setShareType(IShare::TYPE_GROUP)
3963
+            ->setSharedWith('group1')
3964
+            ->setPermissions(Constants::PERMISSION_READ)
3965
+            ->setNode($folder);
3966
+
3967
+        // note: updateShare will modify the received instance but getSharedWith will reread from the database,
3968
+        // so their values will be different
3969
+        $incomingShare = Server::get(IManager::class)->newShare();
3970
+        $incomingShare
3971
+            ->setId(42)
3972
+            ->setSharedBy($this->currentUser)
3973
+            ->setShareOwner($this->currentUser)
3974
+            ->setShareType(IShare::TYPE_GROUP)
3975
+            ->setSharedWith('group1')
3976
+            ->setPermissions(Constants::PERMISSION_READ)
3977
+            ->setNode($folder);
3978
+
3979
+        $this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share);
3980
+
3981
+        $this->shareManager->expects($this->any())
3982
+            ->method('getSharedWith')
3983
+            ->willReturnMap([
3984
+                ['currentUser', IShare::TYPE_USER, $share->getNode(), -1, 0, []],
3985
+                ['currentUser', IShare::TYPE_GROUP, $share->getNode(), -1, 0, [$incomingShare]]
3986
+            ]);
3987
+
3988
+        $this->shareManager->expects($this->once())
3989
+            ->method('updateShare')
3990
+            ->with($share)
3991
+            ->willReturn($share);
3992
+
3993
+        $this->rootFolder->method('getUserFolder')
3994
+            ->with($this->currentUser)
3995
+            ->willReturn($userFolder);
3996
+
3997
+        $userFolder->method('getById')
3998
+            ->with(42)
3999
+            ->willReturn([$folder]);
4000
+
4001
+        $mountPoint = $this->createMock(IMountPoint::class);
4002
+        $folder->method('getMountPoint')
4003
+            ->willReturn($mountPoint);
4004
+        $mountPoint->method('getStorageRootId')
4005
+            ->willReturn(42);
4006
+
4007
+        $result = $ocs->updateShare(42, 31);
4008
+        $this->assertInstanceOf(DataResponse::class, $result);
4009
+    }
4010
+
4011
+    public function testUpdateShareOwnerless(): void {
4012
+        $ocs = $this->mockFormatShare();
4013
+
4014
+        $mount = $this->createMock(IShareOwnerlessMount::class);
4015
+
4016
+        $file = $this->createMock(File::class);
4017
+        $file
4018
+            ->expects($this->exactly(2))
4019
+            ->method('getPermissions')
4020
+            ->willReturn(Constants::PERMISSION_SHARE);
4021
+        $file
4022
+            ->expects($this->once())
4023
+            ->method('getMountPoint')
4024
+            ->willReturn($mount);
4025
+
4026
+        $userFolder = $this->createMock(Folder::class);
4027
+        $userFolder->method('getById')
4028
+            ->with(2)
4029
+            ->willReturn([$file]);
4030
+        $userFolder->method('getFirstNodeById')
4031
+            ->with(2)
4032
+            ->willReturn($file);
4033
+
4034
+        $this->rootFolder
4035
+            ->method('getUserFolder')
4036
+            ->with($this->currentUser)
4037
+            ->willReturn($userFolder);
4038
+
4039
+        $share = $this->createMock(IShare::class);
4040
+        $share
4041
+            ->expects($this->once())
4042
+            ->method('getNode')
4043
+            ->willReturn($file);
4044
+        $share
4045
+            ->expects($this->exactly(2))
4046
+            ->method('getNodeId')
4047
+            ->willReturn(2);
4048
+        $share
4049
+            ->expects($this->exactly(2))
4050
+            ->method('getPermissions')
4051
+            ->willReturn(Constants::PERMISSION_SHARE);
4052
+
4053
+        $this->shareManager
4054
+            ->expects($this->once())
4055
+            ->method('getShareById')
4056
+            ->with('ocinternal:1', $this->currentUser)
4057
+            ->willReturn($share);
4058
+
4059
+        $this->shareManager
4060
+            ->expects($this->once())
4061
+            ->method('updateShare')
4062
+            ->with($share)
4063
+            ->willReturn($share);
4064
+
4065
+        $result = $ocs->updateShare(1, Constants::PERMISSION_ALL);
4066
+        $this->assertInstanceOf(DataResponse::class, $result);
4067
+    }
4068
+
4069
+    public static function dataFormatShare(): array {
4070
+        $owner = ['getDisplayName' => 'ownerDN'];
4071
+        $initiator = ['getDisplayName' => 'initiatorDN'];
4072
+        $recipient = [
4073
+            'getDisplayName' => 'recipientDN',
4074
+            'getSystemEMailAddress' => 'recipient'
4075
+        ];
4076
+
4077
+        $folder = [
4078
+            'class' => Folder::class,
4079
+            'mimeType' => 'myFolderMimeType',
4080
+            'path' => 'folder',
4081
+            'id' => 2,
4082
+        ];
4083
+        $file = [
4084
+            'class' => File::class,
4085
+            'mimeType' => 'myMimeType',
4086
+            'path' => 'file',
4087
+            'id' => 3,
4088
+        ];
4089
+        $fileWithPreview = [
4090
+            'class' => File::class,
4091
+            'mimeType' => 'mimeWithPreview',
4092
+            'path' => 'fileWithPreview',
4093
+            'id' => 4,
4094
+        ];
4095
+
4096
+        $result = [];
4097
+
4098
+        $share = [
4099
+            'type' => IShare::TYPE_USER,
4100
+            'owner' => 'owner',
4101
+            'sharedWith' => 'recipient',
4102
+            'attributes' => [
4103
+                'scope' => 'permissions',
4104
+                'key' => 'download',
4105
+                'value' => true
4106
+            ],
4107
+            'node' => $file,
4108
+            'note' => 'personal note',
4109
+        ];
4110
+
4111
+        // User backend down
4112
+        $result[] = [
4113
+            [
4114
+                'id' => '42',
4115
+                'share_type' => IShare::TYPE_USER,
4116
+                'uid_owner' => 'initiator',
4117
+                'displayname_owner' => 'initiator',
4118
+                'permissions' => 1,
4119
+                'stime' => 946684862,
4120
+                'parent' => null,
4121
+                'expiration' => null,
4122
+                'token' => null,
4123
+                'uid_file_owner' => 'owner',
4124
+                'displayname_file_owner' => 'owner',
4125
+                'path' => 'file',
4126
+                'item_type' => 'file',
4127
+                'storage_id' => 'storageId',
4128
+                'storage' => 100,
4129
+                'item_source' => 3,
4130
+                'file_source' => 3,
4131
+                'file_parent' => 1,
4132
+                'file_target' => 'myTarget',
4133
+                'share_with' => 'recipient',
4134
+                'share_with_displayname' => 'recipient',
4135
+                'share_with_displayname_unique' => 'recipient',
4136
+                'note' => 'personal note',
4137
+                'label' => '',
4138
+                'mail_send' => 0,
4139
+                'mimetype' => 'myMimeType',
4140
+                'has_preview' => false,
4141
+                'hide_download' => 0,
4142
+                'can_edit' => false,
4143
+                'can_delete' => false,
4144
+                'item_size' => 123456,
4145
+                'item_mtime' => 1234567890,
4146
+                'is-mount-root' => false,
4147
+                'mount-type' => '',
4148
+                'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4149
+                'item_permissions' => 1,
4150
+            ],
4151
+            $share,
4152
+            [], false
4153
+        ];
4154
+        // User backend up
4155
+        $result[] = [
4156
+            [
4157
+                'id' => '42',
4158
+                'share_type' => IShare::TYPE_USER,
4159
+                'uid_owner' => 'initiator',
4160
+                'displayname_owner' => 'initiatorDN',
4161
+                'permissions' => 1,
4162
+                'stime' => 946684862,
4163
+                'parent' => null,
4164
+                'expiration' => null,
4165
+                'token' => null,
4166
+                'uid_file_owner' => 'owner',
4167
+                'displayname_file_owner' => 'ownerDN',
4168
+                'note' => 'personal note',
4169
+                'label' => '',
4170
+                'path' => 'file',
4171
+                'item_type' => 'file',
4172
+                'storage_id' => 'storageId',
4173
+                'storage' => 100,
4174
+                'item_source' => 3,
4175
+                'file_source' => 3,
4176
+                'file_parent' => 1,
4177
+                'file_target' => 'myTarget',
4178
+                'share_with' => 'recipient',
4179
+                'share_with_displayname' => 'recipientDN',
4180
+                'share_with_displayname_unique' => 'recipient',
4181
+                'mail_send' => 0,
4182
+                'mimetype' => 'myMimeType',
4183
+                'has_preview' => false,
4184
+                'hide_download' => 0,
4185
+                'can_edit' => false,
4186
+                'can_delete' => false,
4187
+                'item_size' => 123456,
4188
+                'item_mtime' => 1234567890,
4189
+                'is-mount-root' => false,
4190
+                'mount-type' => '',
4191
+                'attributes' => '[{"scope":"permissions","key":"download","value":true}]',
4192
+                'item_permissions' => 1,
4193
+            ], $share, [
4194
+                ['owner', $owner],
4195
+                ['initiator', $initiator],
4196
+                ['recipient', $recipient],
4197
+            ], false
4198
+        ];
4199
+
4200
+        // Same but no attributes
4201
+        $share = [
4202
+            'type' => IShare::TYPE_USER,
4203
+            'owner' => 'owner',
4204
+            'sharedWith' => 'recipient',
4205
+            'node' => $file,
4206
+            'note' => 'personal note',
4207
+        ];
4208
+
4209
+        // User backend down
4210
+        $result[] = [
4211
+            [
4212
+                'id' => '42',
4213
+                'share_type' => IShare::TYPE_USER,
4214
+                'uid_owner' => 'initiator',
4215
+                'displayname_owner' => 'initiator',
4216
+                'permissions' => 1,
4217
+                'attributes' => null,
4218
+                'stime' => 946684862,
4219
+                'parent' => null,
4220
+                'expiration' => null,
4221
+                'token' => null,
4222
+                'uid_file_owner' => 'owner',
4223
+                'displayname_file_owner' => 'owner',
4224
+                'note' => 'personal note',
4225
+                'label' => '',
4226
+                'path' => 'file',
4227
+                'item_type' => 'file',
4228
+                'storage_id' => 'storageId',
4229
+                'storage' => 100,
4230
+                'item_source' => 3,
4231
+                'file_source' => 3,
4232
+                'file_parent' => 1,
4233
+                'file_target' => 'myTarget',
4234
+                'share_with' => 'recipient',
4235
+                'share_with_displayname' => 'recipient',
4236
+                'share_with_displayname_unique' => 'recipient',
4237
+                'mail_send' => 0,
4238
+                'mimetype' => 'myMimeType',
4239
+                'has_preview' => false,
4240
+                'hide_download' => 0,
4241
+                'can_edit' => false,
4242
+                'can_delete' => false,
4243
+                'item_size' => 123456,
4244
+                'item_mtime' => 1234567890,
4245
+                'is-mount-root' => false,
4246
+                'mount-type' => '',
4247
+                'attributes' => null,
4248
+                'item_permissions' => 1,
4249
+            ], $share, [], false
4250
+        ];
4251
+
4252
+        $share['owner'] = 'currentUser';
4253
+
4254
+        // User backend down
4255
+        $result[] = [
4256
+            [
4257
+                'id' => '42',
4258
+                'share_type' => IShare::TYPE_USER,
4259
+                'uid_owner' => 'initiator',
4260
+                'displayname_owner' => 'initiator',
4261
+                'permissions' => 1,
4262
+                'attributes' => null,
4263
+                'stime' => 946684862,
4264
+                'parent' => null,
4265
+                'expiration' => null,
4266
+                'token' => null,
4267
+                'uid_file_owner' => 'currentUser',
4268
+                'displayname_file_owner' => 'currentUser',
4269
+                'note' => 'personal note',
4270
+                'label' => '',
4271
+                'path' => 'file',
4272
+                'item_type' => 'file',
4273
+                'storage_id' => 'storageId',
4274
+                'storage' => 100,
4275
+                'item_source' => 3,
4276
+                'file_source' => 3,
4277
+                'file_parent' => 1,
4278
+                'file_target' => 'myTarget',
4279
+                'share_with' => 'recipient',
4280
+                'share_with_displayname' => 'recipient',
4281
+                'share_with_displayname_unique' => 'recipient',
4282
+                'mail_send' => 0,
4283
+                'mimetype' => 'myMimeType',
4284
+                'has_preview' => false,
4285
+                'hide_download' => 0,
4286
+                'can_edit' => true,
4287
+                'can_delete' => true,
4288
+                'item_size' => 123456,
4289
+                'item_mtime' => 1234567890,
4290
+                'is-mount-root' => false,
4291
+                'mount-type' => '',
4292
+                'attributes' => null,
4293
+                'item_permissions' => 11,
4294
+            ], $share, [], false
4295
+        ];
4296
+
4297
+        // with existing group
4298
+        $share = [
4299
+            'type' => IShare::TYPE_GROUP,
4300
+            'owner' => 'owner',
4301
+            'sharedWith' => 'recipientGroup',
4302
+            'node' => $file,
4303
+            'note' => 'personal note',
4304
+        ];
4305
+
4306
+        $result[] = [
4307
+            [
4308
+                'id' => '42',
4309
+                'share_type' => IShare::TYPE_GROUP,
4310
+                'uid_owner' => 'initiator',
4311
+                'displayname_owner' => 'initiator',
4312
+                'permissions' => 1,
4313
+                'attributes' => null,
4314
+                'stime' => 946684862,
4315
+                'parent' => null,
4316
+                'expiration' => null,
4317
+                'token' => null,
4318
+                'uid_file_owner' => 'owner',
4319
+                'displayname_file_owner' => 'owner',
4320
+                'note' => 'personal note',
4321
+                'label' => '',
4322
+                'path' => 'file',
4323
+                'item_type' => 'file',
4324
+                'storage_id' => 'storageId',
4325
+                'storage' => 100,
4326
+                'item_source' => 3,
4327
+                'file_source' => 3,
4328
+                'file_parent' => 1,
4329
+                'file_target' => 'myTarget',
4330
+                'share_with' => 'recipientGroup',
4331
+                'share_with_displayname' => 'recipientGroupDisplayName',
4332
+                'mail_send' => 0,
4333
+                'mimetype' => 'myMimeType',
4334
+                'has_preview' => false,
4335
+                'hide_download' => 0,
4336
+                'can_edit' => false,
4337
+                'can_delete' => false,
4338
+                'item_size' => 123456,
4339
+                'item_mtime' => 1234567890,
4340
+                'is-mount-root' => false,
4341
+                'mount-type' => '',
4342
+                'attributes' => null,
4343
+                'item_permissions' => 1,
4344
+            ], $share, [], false
4345
+        ];
4346
+
4347
+        // with unknown group / no group backend
4348
+        $share['sharedWith'] = 'recipientGroup2';
4349
+
4350
+        $result[] = [
4351
+            [
4352
+                'id' => '42',
4353
+                'share_type' => IShare::TYPE_GROUP,
4354
+                'uid_owner' => 'initiator',
4355
+                'displayname_owner' => 'initiator',
4356
+                'permissions' => 1,
4357
+                'stime' => 946684862,
4358
+                'parent' => null,
4359
+                'expiration' => null,
4360
+                'token' => null,
4361
+                'uid_file_owner' => 'owner',
4362
+                'displayname_file_owner' => 'owner',
4363
+                'note' => 'personal note',
4364
+                'label' => '',
4365
+                'path' => 'file',
4366
+                'item_type' => 'file',
4367
+                'storage_id' => 'storageId',
4368
+                'storage' => 100,
4369
+                'item_source' => 3,
4370
+                'file_source' => 3,
4371
+                'file_parent' => 1,
4372
+                'file_target' => 'myTarget',
4373
+                'share_with' => 'recipientGroup2',
4374
+                'share_with_displayname' => 'recipientGroup2',
4375
+                'mail_send' => 0,
4376
+                'mimetype' => 'myMimeType',
4377
+                'has_preview' => false,
4378
+                'hide_download' => 0,
4379
+                'can_edit' => false,
4380
+                'can_delete' => false,
4381
+                'item_size' => 123456,
4382
+                'item_mtime' => 1234567890,
4383
+                'is-mount-root' => false,
4384
+                'mount-type' => '',
4385
+                'attributes' => null,
4386
+                'item_permissions' => 1,
4387
+            ], $share, [], false
4388
+        ];
4389
+
4390
+        $share = [
4391
+            'type' => IShare::TYPE_LINK,
4392
+            'owner' => 'owner',
4393
+            'node' => $file,
4394
+            'note' => 'personal note',
4395
+            'password' => 'mypassword',
4396
+            'expirationDate' => new \DateTime('2001-01-02T00:00:00'),
4397
+            'token' => 'myToken',
4398
+            'label' => 'new link share',
4399
+        ];
4400
+
4401
+        $result[] = [
4402
+            [
4403
+                'id' => '42',
4404
+                'share_type' => IShare::TYPE_LINK,
4405
+                'uid_owner' => 'initiator',
4406
+                'displayname_owner' => 'initiator',
4407
+                'permissions' => 1,
4408
+                'attributes' => null,
4409
+                'stime' => 946684862,
4410
+                'parent' => null,
4411
+                'expiration' => '2001-01-02 00:00:00',
4412
+                'token' => 'myToken',
4413
+                'uid_file_owner' => 'owner',
4414
+                'displayname_file_owner' => 'owner',
4415
+                'note' => 'personal note',
4416
+                'label' => 'new link share',
4417
+                'path' => 'file',
4418
+                'item_type' => 'file',
4419
+                'storage_id' => 'storageId',
4420
+                'storage' => 100,
4421
+                'item_source' => 3,
4422
+                'file_source' => 3,
4423
+                'file_parent' => 1,
4424
+                'file_target' => 'myTarget',
4425
+                'password' => 'mypassword',
4426
+                'share_with' => 'mypassword',
4427
+                'share_with_displayname' => '(Shared link)',
4428
+                'send_password_by_talk' => false,
4429
+                'mail_send' => 0,
4430
+                'url' => 'myLink',
4431
+                'mimetype' => 'myMimeType',
4432
+                'has_preview' => false,
4433
+                'hide_download' => 0,
4434
+                'can_edit' => false,
4435
+                'can_delete' => false,
4436
+                'item_size' => 123456,
4437
+                'item_mtime' => 1234567890,
4438
+                'is-mount-root' => false,
4439
+                'mount-type' => '',
4440
+                'attributes' => null,
4441
+                'item_permissions' => 1,
4442
+            ], $share, [], false
4443
+        ];
4444
+
4445
+        $share['sendPasswordByTalk'] = true;
4446
+
4447
+        $result[] = [
4448
+            [
4449
+                'id' => '42',
4450
+                'share_type' => IShare::TYPE_LINK,
4451
+                'uid_owner' => 'initiator',
4452
+                'displayname_owner' => 'initiator',
4453
+                'permissions' => 1,
4454
+                'stime' => 946684862,
4455
+                'parent' => null,
4456
+                'expiration' => '2001-01-02 00:00:00',
4457
+                'token' => 'myToken',
4458
+                'uid_file_owner' => 'owner',
4459
+                'displayname_file_owner' => 'owner',
4460
+                'note' => 'personal note',
4461
+                'label' => 'new link share',
4462
+                'path' => 'file',
4463
+                'item_type' => 'file',
4464
+                'storage_id' => 'storageId',
4465
+                'storage' => 100,
4466
+                'item_source' => 3,
4467
+                'file_source' => 3,
4468
+                'file_parent' => 1,
4469
+                'file_target' => 'myTarget',
4470
+                'password' => 'mypassword',
4471
+                'share_with' => 'mypassword',
4472
+                'share_with_displayname' => '(Shared link)',
4473
+                'send_password_by_talk' => true,
4474
+                'mail_send' => 0,
4475
+                'url' => 'myLink',
4476
+                'mimetype' => 'myMimeType',
4477
+                'has_preview' => false,
4478
+                'hide_download' => 0,
4479
+                'can_edit' => false,
4480
+                'can_delete' => false,
4481
+                'item_size' => 123456,
4482
+                'item_mtime' => 1234567890,
4483
+                'is-mount-root' => false,
4484
+                'mount-type' => '',
4485
+                'attributes' => null,
4486
+                'item_permissions' => 1,
4487
+            ], $share, [], false
4488
+        ];
4489
+
4490
+        $share = [
4491
+            'type' => IShare::TYPE_REMOTE,
4492
+            'owner' => 'owner',
4493
+            'sharedWith' => '[email protected]',
4494
+            'node' => $folder,
4495
+            'note' => 'personal note',
4496
+            'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4497
+        ];
4498
+
4499
+        $result[] = [
4500
+            [
4501
+                'id' => '42',
4502
+                'share_type' => IShare::TYPE_REMOTE,
4503
+                'uid_owner' => 'initiator',
4504
+                'displayname_owner' => 'initiator',
4505
+                'permissions' => 1,
4506
+                'stime' => 946684862,
4507
+                'parent' => null,
4508
+                'expiration' => '2001-02-03 00:00:00',
4509
+                'token' => null,
4510
+                'uid_file_owner' => 'owner',
4511
+                'displayname_file_owner' => 'owner',
4512
+                'note' => 'personal note',
4513
+                'label' => '',
4514
+                'path' => 'folder',
4515
+                'item_type' => 'folder',
4516
+                'storage_id' => 'storageId',
4517
+                'storage' => 100,
4518
+                'item_source' => 2,
4519
+                'file_source' => 2,
4520
+                'file_parent' => 1,
4521
+                'file_target' => 'myTarget',
4522
+                'share_with' => '[email protected]',
4523
+                'share_with_displayname' => 'foobar',
4524
+                'mail_send' => 0,
4525
+                'mimetype' => 'myFolderMimeType',
4526
+                'has_preview' => false,
4527
+                'hide_download' => 0,
4528
+                'can_edit' => false,
4529
+                'can_delete' => false,
4530
+                'item_size' => 123456,
4531
+                'item_mtime' => 1234567890,
4532
+                'is-mount-root' => false,
4533
+                'mount-type' => '',
4534
+                'attributes' => null,
4535
+                'item_permissions' => 1,
4536
+                'is_trusted_server' => false,
4537
+            ], $share, [], false
4538
+        ];
4539
+
4540
+        $share = [
4541
+            'type' => IShare::TYPE_REMOTE_GROUP,
4542
+            'owner' => 'owner',
4543
+            'sharedWith' => '[email protected]',
4544
+            'node' => $folder,
4545
+            'note' => 'personal note',
4546
+            'expirationDate' => new \DateTime('2001-02-03T04:05:06'),
4547
+        ];
4548
+
4549
+        $result[] = [
4550
+            [
4551
+                'id' => '42',
4552
+                'share_type' => IShare::TYPE_REMOTE_GROUP,
4553
+                'uid_owner' => 'initiator',
4554
+                'displayname_owner' => 'initiator',
4555
+                'permissions' => 1,
4556
+                'stime' => 946684862,
4557
+                'parent' => null,
4558
+                'expiration' => '2001-02-03 00:00:00',
4559
+                'token' => null,
4560
+                'uid_file_owner' => 'owner',
4561
+                'displayname_file_owner' => 'owner',
4562
+                'note' => 'personal note',
4563
+                'label' => '',
4564
+                'path' => 'folder',
4565
+                'item_type' => 'folder',
4566
+                'storage_id' => 'storageId',
4567
+                'storage' => 100,
4568
+                'item_source' => 2,
4569
+                'file_source' => 2,
4570
+                'file_parent' => 1,
4571
+                'file_target' => 'myTarget',
4572
+                'share_with' => '[email protected]',
4573
+                'share_with_displayname' => 'foobar',
4574
+                'mail_send' => 0,
4575
+                'mimetype' => 'myFolderMimeType',
4576
+                'has_preview' => false,
4577
+                'hide_download' => 0,
4578
+                'can_edit' => false,
4579
+                'can_delete' => false,
4580
+                'item_size' => 123456,
4581
+                'item_mtime' => 1234567890,
4582
+                'is-mount-root' => false,
4583
+                'mount-type' => '',
4584
+                'attributes' => null,
4585
+                'item_permissions' => 1,
4586
+                'is_trusted_server' => false,
4587
+            ], $share, [], false
4588
+        ];
4589
+
4590
+        // Circle with id, display name and avatar set by the Circles app
4591
+        $share = [
4592
+            'type' => IShare::TYPE_CIRCLE,
4593
+            'owner' => 'owner',
4594
+            'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4595
+            'sharedWithDisplayName' => 'The display name',
4596
+            'sharedWithAvatar' => 'path/to/the/avatar',
4597
+            'node' => $folder,
4598
+        ];
4599
+
4600
+        $result[] = [
4601
+            [
4602
+                'id' => '42',
4603
+                'share_type' => IShare::TYPE_CIRCLE,
4604
+                'uid_owner' => 'initiator',
4605
+                'displayname_owner' => 'initiator',
4606
+                'permissions' => 1,
4607
+                'attributes' => null,
4608
+                'stime' => 946684862,
4609
+                'parent' => null,
4610
+                'expiration' => null,
4611
+                'token' => null,
4612
+                'uid_file_owner' => 'owner',
4613
+                'displayname_file_owner' => 'owner',
4614
+                'note' => '',
4615
+                'label' => '',
4616
+                'path' => 'folder',
4617
+                'item_type' => 'folder',
4618
+                'storage_id' => 'storageId',
4619
+                'storage' => 100,
4620
+                'item_source' => 2,
4621
+                'file_source' => 2,
4622
+                'file_parent' => 1,
4623
+                'file_target' => 'myTarget',
4624
+                'share_with' => '4815162342',
4625
+                'share_with_displayname' => 'The display name',
4626
+                'share_with_avatar' => 'path/to/the/avatar',
4627
+                'mail_send' => 0,
4628
+                'mimetype' => 'myFolderMimeType',
4629
+                'has_preview' => false,
4630
+                'hide_download' => 0,
4631
+                'can_edit' => false,
4632
+                'can_delete' => false,
4633
+                'item_size' => 123456,
4634
+                'item_mtime' => 1234567890,
4635
+                'is-mount-root' => false,
4636
+                'mount-type' => '',
4637
+                'attributes' => null,
4638
+                'item_permissions' => 1,
4639
+            ], $share, [], false
4640
+        ];
4641
+
4642
+        // Circle with id set by the Circles app
4643
+        $share = [
4644
+            'type' => IShare::TYPE_CIRCLE,
4645
+            'owner' => 'owner',
4646
+            'sharedWith' => 'Circle (Public circle, circleOwner) [4815162342]',
4647
+            'node' => $folder,
4648
+        ];
4649
+
4650
+        $result[] = [
4651
+            [
4652
+                'id' => '42',
4653
+                'share_type' => IShare::TYPE_CIRCLE,
4654
+                'uid_owner' => 'initiator',
4655
+                'displayname_owner' => 'initiator',
4656
+                'permissions' => 1,
4657
+                'stime' => 946684862,
4658
+                'parent' => null,
4659
+                'expiration' => null,
4660
+                'token' => null,
4661
+                'uid_file_owner' => 'owner',
4662
+                'displayname_file_owner' => 'owner',
4663
+                'note' => '',
4664
+                'label' => '',
4665
+                'path' => 'folder',
4666
+                'item_type' => 'folder',
4667
+                'storage_id' => 'storageId',
4668
+                'storage' => 100,
4669
+                'item_source' => 2,
4670
+                'file_source' => 2,
4671
+                'file_parent' => 1,
4672
+                'file_target' => 'myTarget',
4673
+                'share_with' => '4815162342',
4674
+                'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4675
+                'share_with_avatar' => '',
4676
+                'mail_send' => 0,
4677
+                'mimetype' => 'myFolderMimeType',
4678
+                'has_preview' => false,
4679
+                'hide_download' => 0,
4680
+                'can_edit' => false,
4681
+                'can_delete' => false,
4682
+                'item_size' => 123456,
4683
+                'item_mtime' => 1234567890,
4684
+                'is-mount-root' => false,
4685
+                'mount-type' => '',
4686
+                'attributes' => null,
4687
+                'item_permissions' => 1,
4688
+            ], $share, [], false
4689
+        ];
4690
+
4691
+        // Circle with id not set by the Circles app
4692
+        $share = [
4693
+            'type' => IShare::TYPE_CIRCLE,
4694
+            'owner' => 'owner',
4695
+            'sharedWith' => 'Circle (Public circle, circleOwner)',
4696
+            'node' => $folder,
4697
+        ];
4698
+
4699
+        $result[] = [
4700
+            [
4701
+                'id' => '42',
4702
+                'share_type' => IShare::TYPE_CIRCLE,
4703
+                'uid_owner' => 'initiator',
4704
+                'displayname_owner' => 'initiator',
4705
+                'permissions' => 1,
4706
+                'stime' => 946684862,
4707
+                'parent' => null,
4708
+                'expiration' => null,
4709
+                'token' => null,
4710
+                'uid_file_owner' => 'owner',
4711
+                'displayname_file_owner' => 'owner',
4712
+                'note' => '',
4713
+                'label' => '',
4714
+                'path' => 'folder',
4715
+                'item_type' => 'folder',
4716
+                'storage_id' => 'storageId',
4717
+                'storage' => 100,
4718
+                'item_source' => 2,
4719
+                'file_source' => 2,
4720
+                'file_parent' => 1,
4721
+                'file_target' => 'myTarget',
4722
+                'share_with' => 'Circle',
4723
+                'share_with_displayname' => 'Circle (Public circle, circleOwner)',
4724
+                'share_with_avatar' => '',
4725
+                'mail_send' => 0,
4726
+                'mimetype' => 'myFolderMimeType',
4727
+                'has_preview' => false,
4728
+                'hide_download' => 0,
4729
+                'can_edit' => false,
4730
+                'can_delete' => false,
4731
+                'item_size' => 123456,
4732
+                'item_mtime' => 1234567890,
4733
+                'is-mount-root' => false,
4734
+                'mount-type' => '',
4735
+                'attributes' => null,
4736
+                'item_permissions' => 1,
4737
+            ], $share, [], false
4738
+        ];
4739
+
4740
+        // No node
4741
+        $share = [
4742
+            'type' => IShare::TYPE_USER,
4743
+            'owner' => 'owner',
4744
+            'sharedWith' => 'recipient',
4745
+            'note' => 'personal note',
4746
+        ];
4747
+
4748
+        $result[] = [
4749
+            [], $share, [], true
4750
+        ];
4751
+
4752
+        $share = [
4753
+            'type' => IShare::TYPE_EMAIL,
4754
+            'owner' => 'owner',
4755
+            'sharedWith' => '[email protected]',
4756
+            'node' => $folder,
4757
+            'password' => 'password',
4758
+        ];
4759
+
4760
+        $result[] = [
4761
+            [
4762
+                'id' => '42',
4763
+                'share_type' => IShare::TYPE_EMAIL,
4764
+                'uid_owner' => 'initiator',
4765
+                'displayname_owner' => 'initiator',
4766
+                'permissions' => 1,
4767
+                'stime' => 946684862,
4768
+                'parent' => null,
4769
+                'expiration' => null,
4770
+                'token' => null,
4771
+                'uid_file_owner' => 'owner',
4772
+                'displayname_file_owner' => 'owner',
4773
+                'note' => '',
4774
+                'label' => '',
4775
+                'path' => 'folder',
4776
+                'item_type' => 'folder',
4777
+                'storage_id' => 'storageId',
4778
+                'storage' => 100,
4779
+                'item_source' => 2,
4780
+                'file_source' => 2,
4781
+                'file_parent' => 1,
4782
+                'file_target' => 'myTarget',
4783
+                'share_with' => '[email protected]',
4784
+                'share_with_displayname' => 'mail display name',
4785
+                'mail_send' => 0,
4786
+                'mimetype' => 'myFolderMimeType',
4787
+                'has_preview' => false,
4788
+                'password' => 'password',
4789
+                'send_password_by_talk' => false,
4790
+                'hide_download' => 0,
4791
+                'can_edit' => false,
4792
+                'can_delete' => false,
4793
+                'password_expiration_time' => null,
4794
+                'item_size' => 123456,
4795
+                'item_mtime' => 1234567890,
4796
+                'is-mount-root' => false,
4797
+                'mount-type' => '',
4798
+                'attributes' => null,
4799
+                'item_permissions' => 1,
4800
+            ], $share, [], false
4801
+        ];
4802
+
4803
+        $share['sendPasswordByTalk'] = true;
4804
+
4805
+        $result[] = [
4806
+            [
4807
+                'id' => '42',
4808
+                'share_type' => IShare::TYPE_EMAIL,
4809
+                'uid_owner' => 'initiator',
4810
+                'displayname_owner' => 'initiator',
4811
+                'permissions' => 1,
4812
+                'stime' => 946684862,
4813
+                'parent' => null,
4814
+                'expiration' => null,
4815
+                'token' => null,
4816
+                'uid_file_owner' => 'owner',
4817
+                'displayname_file_owner' => 'owner',
4818
+                'note' => '',
4819
+                'label' => '',
4820
+                'path' => 'folder',
4821
+                'item_type' => 'folder',
4822
+                'storage_id' => 'storageId',
4823
+                'storage' => 100,
4824
+                'item_source' => 2,
4825
+                'file_source' => 2,
4826
+                'file_parent' => 1,
4827
+                'file_target' => 'myTarget',
4828
+                'share_with' => '[email protected]',
4829
+                'share_with_displayname' => 'mail display name',
4830
+                'mail_send' => 0,
4831
+                'mimetype' => 'myFolderMimeType',
4832
+                'has_preview' => false,
4833
+                'password' => 'password',
4834
+                'send_password_by_talk' => true,
4835
+                'hide_download' => 0,
4836
+                'can_edit' => false,
4837
+                'can_delete' => false,
4838
+                'password_expiration_time' => null,
4839
+                'item_size' => 123456,
4840
+                'item_mtime' => 1234567890,
4841
+                'is-mount-root' => false,
4842
+                'mount-type' => '',
4843
+                'attributes' => null,
4844
+                'item_permissions' => 1,
4845
+            ], $share, [], false
4846
+        ];
4847
+
4848
+        // Preview is available
4849
+        $share = [
4850
+            'type' => IShare::TYPE_USER,
4851
+            'owner' => 'currentUser',
4852
+            'sharedWith' => 'recipient',
4853
+            'node' => $fileWithPreview,
4854
+            'note' => 'personal note',
4855
+        ];
4856
+
4857
+        $result[] = [
4858
+            [
4859
+                'id' => '42',
4860
+                'share_type' => IShare::TYPE_USER,
4861
+                'uid_owner' => 'initiator',
4862
+                'displayname_owner' => 'initiator',
4863
+                'permissions' => 1,
4864
+                'stime' => 946684862,
4865
+                'parent' => null,
4866
+                'expiration' => null,
4867
+                'token' => null,
4868
+                'uid_file_owner' => 'currentUser',
4869
+                'displayname_file_owner' => 'currentUser',
4870
+                'note' => 'personal note',
4871
+                'label' => '',
4872
+                'path' => 'fileWithPreview',
4873
+                'item_type' => 'file',
4874
+                'storage_id' => 'storageId',
4875
+                'storage' => 100,
4876
+                'item_source' => 4,
4877
+                'file_source' => 4,
4878
+                'file_parent' => 1,
4879
+                'file_target' => 'myTarget',
4880
+                'share_with' => 'recipient',
4881
+                'share_with_displayname' => 'recipient',
4882
+                'share_with_displayname_unique' => 'recipient',
4883
+                'mail_send' => 0,
4884
+                'mimetype' => 'mimeWithPreview',
4885
+                'has_preview' => true,
4886
+                'hide_download' => 0,
4887
+                'can_edit' => true,
4888
+                'can_delete' => true,
4889
+                'item_size' => 123456,
4890
+                'item_mtime' => 1234567890,
4891
+                'is-mount-root' => false,
4892
+                'mount-type' => '',
4893
+                'attributes' => null,
4894
+                'item_permissions' => 11,
4895
+            ], $share, [], false
4896
+        ];
4897
+
4898
+        return $result;
4899
+    }
4900
+
4901
+    #[DataProvider('dataFormatShare')]
4902
+    public function testFormatShare(
4903
+        array $expects,
4904
+        array $shareParams,
4905
+        array $users,
4906
+        bool $exception,
4907
+    ): void {
4908
+        $users = array_map(
4909
+            function ($user) {
4910
+                $mock = $this->createMock(IUser::class);
4911
+                foreach ($user[1] as $method => $return) {
4912
+                    $mock->method($method)->willReturn($return);
4913
+                }
4914
+                return [$user[0],$mock];
4915
+            },
4916
+            $users
4917
+        );
4918
+
4919
+        $share = Server::get(IManager::class)->newShare();
4920
+        $share->setShareType($shareParams['type'])
4921
+            ->setSharedBy('initiator')
4922
+            ->setShareOwner($shareParams['owner'])
4923
+            ->setPermissions(Constants::PERMISSION_READ)
4924
+            ->setShareTime(new \DateTime('2000-01-01T00:01:02'))
4925
+            ->setTarget('myTarget')
4926
+            ->setId(42);
4927
+        if (isset($shareParams['sharedWith'])) {
4928
+            $share->setSharedWith($shareParams['sharedWith']);
4929
+        }
4930
+        if (isset($shareParams['sharedWithDisplayName'])) {
4931
+            $share->setSharedWithDisplayName($shareParams['sharedWithDisplayName']);
4932
+        }
4933
+        if (isset($shareParams['sharedWithAvatar'])) {
4934
+            $share->setSharedWithAvatar($shareParams['sharedWithAvatar']);
4935
+        }
4936
+        if (isset($shareParams['attributes'])) {
4937
+            $shareAttributes = $this->createMock(IShareAttributes::class);
4938
+            $shareAttributes->method('toArray')->willReturn($shareParams['attributes']);
4939
+            $shareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
4940
+            $share->setAttributes($shareAttributes);
4941
+
4942
+            $expects['attributes'] = \json_encode($shareParams['attributes']);
4943
+        }
4944
+        if (isset($shareParams['node'])) {
4945
+            $node = $this->createMock($shareParams['node']['class']);
4946
+
4947
+            $node->method('getMimeType')->willReturn($shareParams['node']['mimeType']);
4948
+
4949
+            $mountPoint = $this->createMock(IMountPoint::class);
4950
+            $mountPoint->method('getMountType')->willReturn('');
4951
+            $node->method('getMountPoint')->willReturn($mountPoint);
4952
+
4953
+            $node->method('getPath')->willReturn($shareParams['node']['path']);
4954
+            $node->method('getId')->willReturn($shareParams['node']['id']);
4955
+
4956
+            $parent = $this->createMock(Folder::class);
4957
+            $parent->method('getId')->willReturn(1);
4958
+            $node->method('getParent')->willReturn($parent);
4959
+
4960
+            $node->method('getSize')->willReturn(123456);
4961
+            $node->method('getMTime')->willReturn(1234567890);
4962
+
4963
+            $cache = $this->createMock(ICache::class);
4964
+            $cache->method('getNumericStorageId')->willReturn(100);
4965
+            $storage = $this->createMock(IStorage::class);
4966
+            $storage->method('getId')->willReturn('storageId');
4967
+            $storage->method('getCache')->willReturn($cache);
4968
+
4969
+            $node->method('getStorage')->willReturn($storage);
4970
+
4971
+            $share->setNode($node);
4972
+        }
4973
+        if (isset($shareParams['note'])) {
4974
+            $share->setNote($shareParams['note']);
4975
+        }
4976
+        if (isset($shareParams['expirationDate'])) {
4977
+            $share->setExpirationDate($shareParams['expirationDate']);
4978
+        }
4979
+        if (isset($shareParams['token'])) {
4980
+            $share->setToken($shareParams['token']);
4981
+        }
4982
+        if (isset($shareParams['label'])) {
4983
+            $share->setLabel($shareParams['label']);
4984
+        }
4985
+        if (isset($shareParams['password'])) {
4986
+            $share->setPassword($shareParams['password']);
4987
+        }
4988
+        if (isset($shareParams['sendPasswordByTalk'])) {
4989
+            $share->setSendPasswordByTalk($shareParams['sendPasswordByTalk']);
4990
+        }
4991
+
4992
+        $this->userManager->method('get')->willReturnMap($users);
4993
+
4994
+        $recipientGroup = $this->createMock(IGroup::class);
4995
+        $recipientGroup->method('getDisplayName')->willReturn('recipientGroupDisplayName');
4996
+        $this->groupManager->method('get')->willReturnMap([
4997
+            ['recipientGroup', $recipientGroup],
4998
+        ]);
4999
+
5000
+        $this->urlGenerator->method('linkToRouteAbsolute')
5001
+            ->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken'])
5002
+            ->willReturn('myLink');
5003
+
5004
+        $this->rootFolder->method('getUserFolder')
5005
+            ->with($this->currentUser)
5006
+            ->willReturnSelf();
5007
+        $this->dateTimeZone->method('getTimezone')->willReturn(new \DateTimeZone('UTC'));
5008
+
5009
+        if (!$exception) {
5010
+            $this->rootFolder->method('getFirstNodeById')
5011
+                ->with($share->getNodeId())
5012
+                ->willReturn($share->getNode());
5013
+
5014
+            $this->rootFolder->method('getRelativePath')
5015
+                ->with($share->getNode()->getPath())
5016
+                ->willReturnArgument(0);
5017
+        }
5018
+
5019
+        $cm = $this->createMock(\OCP\Contacts\IManager::class);
5020
+        $this->overwriteService(\OCP\Contacts\IManager::class, $cm);
5021
+
5022
+        $cm->method('search')
5023
+            ->willReturnMap([
5024
+                ['[email protected]', ['CLOUD'], [
5025
+                    'limit' => 1,
5026
+                    'enumeration' => false,
5027
+                    'strict_search' => true,
5028
+                ],
5029
+                    [
5030
+                        [
5031
+                            'CLOUD' => [
5032
+                                '[email protected]',
5033
+                            ],
5034
+                            'FN' => 'foobar',
5035
+                        ],
5036
+                    ],
5037
+                ],
5038
+                ['[email protected]', ['EMAIL'], [
5039
+                    'limit' => 1,
5040
+                    'enumeration' => false,
5041
+                    'strict_search' => true,
5042
+                ],
5043
+                    [
5044
+                        [
5045
+                            'EMAIL' => [
5046
+                                '[email protected]',
5047
+                            ],
5048
+                            'FN' => 'mail display name',
5049
+                        ],
5050
+                    ],
5051
+                ],
5052
+            ]);
5053
+
5054
+        try {
5055
+            $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5056
+            $this->assertFalse($exception);
5057
+            $this->assertEquals($expects, $result);
5058
+        } catch (NotFoundException $e) {
5059
+            $this->assertTrue($exception);
5060
+        }
5061
+    }
5062
+
5063
+    public static function dataFormatRoomShare(): array {
5064
+        $result = [];
5065
+
5066
+        $result[] = [
5067
+            [
5068
+                'id' => '42',
5069
+                'share_type' => IShare::TYPE_ROOM,
5070
+                'uid_owner' => 'initiator',
5071
+                'displayname_owner' => 'initiator',
5072
+                'permissions' => 1,
5073
+                'stime' => 946684862,
5074
+                'parent' => null,
5075
+                'expiration' => null,
5076
+                'token' => null,
5077
+                'uid_file_owner' => 'owner',
5078
+                'displayname_file_owner' => 'owner',
5079
+                'note' => 'personal note',
5080
+                'path' => 'file',
5081
+                'item_type' => 'file',
5082
+                'storage_id' => 'storageId',
5083
+                'storage' => 100,
5084
+                'item_source' => 3,
5085
+                'file_source' => 3,
5086
+                'file_parent' => 1,
5087
+                'file_target' => 'myTarget',
5088
+                'share_with' => 'recipientRoom',
5089
+                'share_with_displayname' => '',
5090
+                'mail_send' => 0,
5091
+                'mimetype' => 'myMimeType',
5092
+                'has_preview' => false,
5093
+                'hide_download' => 0,
5094
+                'label' => '',
5095
+                'can_edit' => false,
5096
+                'can_delete' => false,
5097
+                'item_size' => 123456,
5098
+                'item_mtime' => 1234567890,
5099
+                'is-mount-root' => false,
5100
+                'mount-type' => '',
5101
+                'attributes' => null,
5102
+                'item_permissions' => 1,
5103
+            ], false, []
5104
+        ];
5105
+
5106
+        $result[] = [
5107
+            [
5108
+                'id' => '42',
5109
+                'share_type' => IShare::TYPE_ROOM,
5110
+                'uid_owner' => 'initiator',
5111
+                'displayname_owner' => 'initiator',
5112
+                'permissions' => 1,
5113
+                'stime' => 946684862,
5114
+                'parent' => null,
5115
+                'expiration' => null,
5116
+                'token' => null,
5117
+                'uid_file_owner' => 'owner',
5118
+                'displayname_file_owner' => 'owner',
5119
+                'note' => 'personal note',
5120
+                'path' => 'file',
5121
+                'item_type' => 'file',
5122
+                'storage_id' => 'storageId',
5123
+                'storage' => 100,
5124
+                'item_source' => 3,
5125
+                'file_source' => 3,
5126
+                'file_parent' => 1,
5127
+                'file_target' => 'myTarget',
5128
+                'share_with' => 'recipientRoom',
5129
+                'share_with_displayname' => 'recipientRoomName',
5130
+                'mail_send' => 0,
5131
+                'mimetype' => 'myMimeType',
5132
+                'has_preview' => false,
5133
+                'hide_download' => 0,
5134
+                'label' => '',
5135
+                'can_edit' => false,
5136
+                'can_delete' => false,
5137
+                'item_size' => 123456,
5138
+                'item_mtime' => 1234567890,
5139
+                'is-mount-root' => false,
5140
+                'mount-type' => '',
5141
+                'attributes' => null,
5142
+                'item_permissions' => 9,
5143
+            ], true, [
5144
+                'share_with_displayname' => 'recipientRoomName'
5145
+            ]
5146
+        ];
5147
+
5148
+        return $result;
5149
+    }
5150
+
5151
+    /**
5152
+     *
5153
+     * @param array $expects
5154
+     * @param IShare $share
5155
+     * @param bool $helperAvailable
5156
+     * @param array $formatShareByHelper
5157
+     */
5158
+    #[DataProvider('dataFormatRoomShare')]
5159
+    public function testFormatRoomShare(array $expects, bool $helperAvailable, array $formatShareByHelper): void {
5160
+        $file = $this->createMock(File::class);
5161
+
5162
+        $file->method('getMimeType')->willReturn('myMimeType');
5163
+        $file->method('getPath')->willReturn('file');
5164
+        $file->method('getId')->willReturn(3);
5165
+
5166
+        $parent = $this->createMock(Folder::class);
5167
+        $parent->method('getId')->willReturn(1);
5168
+        $file->method('getParent')->willReturn($parent);
5169
+
5170
+        $file->method('getSize')->willReturn(123456);
5171
+        $file->method('getMTime')->willReturn(1234567890);
5172
+
5173
+        $mountPoint = $this->createMock(IMountPoint::class);
5174
+        $mountPoint->method('getMountType')->willReturn('');
5175
+        $file->method('getMountPoint')->willReturn($mountPoint);
5176
+
5177
+        $cache = $this->createMock(ICache::class);
5178
+        $cache->method('getNumericStorageId')->willReturn(100);
5179
+        $storage = $this->createMock(IStorage::class);
5180
+        $storage->method('getId')->willReturn('storageId');
5181
+        $storage->method('getCache')->willReturn($cache);
5182
+
5183
+        $file->method('getStorage')->willReturn($storage);
5184
+
5185
+        $share = Server::get(IManager::class)->newShare();
5186
+        $share->setShareType(IShare::TYPE_ROOM)
5187
+            ->setSharedWith('recipientRoom')
5188
+            ->setSharedBy('initiator')
5189
+            ->setShareOwner('owner')
5190
+            ->setPermissions(Constants::PERMISSION_READ)
5191
+            ->setNode($file)
5192
+            ->setShareTime(new \DateTime('2000-01-01T00:01:02'))
5193
+            ->setTarget('myTarget')
5194
+            ->setNote('personal note')
5195
+            ->setId(42);
5196
+
5197
+        $this->rootFolder->method('getUserFolder')
5198
+            ->with($this->currentUser)
5199
+            ->willReturnSelf();
5200
+
5201
+        $this->rootFolder->method('getFirstNodeById')
5202
+            ->with($share->getNodeId())
5203
+            ->willReturn($share->getNode());
5204
+
5205
+        $this->rootFolder->method('getRelativePath')
5206
+            ->with($share->getNode()->getPath())
5207
+            ->willReturnArgument(0);
5208
+
5209
+        if (!$helperAvailable) {
5210
+            $this->appManager->method('isEnabledForUser')
5211
+                ->with('spreed')
5212
+                ->willReturn(false);
5213
+        } else {
5214
+            $this->appManager->method('isEnabledForUser')
5215
+                ->with('spreed')
5216
+                ->willReturn(true);
5217
+
5218
+            // This is not possible anymore with PHPUnit 10+
5219
+            // as `setMethods` was removed and now real reflection is used, thus the class needs to exist.
5220
+            // $helper = $this->getMockBuilder('\OCA\Talk\Share\Helper\ShareAPIController')
5221
+            $helper = $this->getMockBuilder(\stdClass::class)
5222
+                ->addMethods(['formatShare', 'canAccessShare'])
5223
+                ->getMock();
5224
+            $helper->method('formatShare')
5225
+                ->with($share)
5226
+                ->willReturn($formatShareByHelper);
5227
+            $helper->method('canAccessShare')
5228
+                ->with($share)
5229
+                ->willReturn(true);
5230
+
5231
+            $this->serverContainer->method('get')
5232
+                ->with('\OCA\Talk\Share\Helper\ShareAPIController')
5233
+                ->willReturn($helper);
5234
+        }
5235
+
5236
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5237
+        $this->assertEquals($expects, $result);
5238
+    }
5239
+
5240
+    /**
5241
+     * @return list{Folder, Folder}
5242
+     */
5243
+    private function getNonSharedUserFolder(): array {
5244
+        $node = $this->getMockBuilder(Folder::class)->getMock();
5245
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
5246
+        $storage = $this->createMock(IStorage::class);
5247
+        $storage->method('instanceOfStorage')
5248
+            ->willReturnMap([
5249
+                ['OCA\Files_Sharing\External\Storage', false],
5250
+                ['OCA\Files_Sharing\SharedStorage', false],
5251
+            ]);
5252
+        $userFolder->method('getStorage')->willReturn($storage);
5253
+        $node->method('getStorage')->willReturn($storage);
5254
+        $node->method('getId')->willReturn(42);
5255
+        $user = $this->createMock(IUser::class);
5256
+        $user->method('getUID')->willReturn($this->currentUser);
5257
+        $node->method('getOwner')->willReturn($user);
5258
+        return [$userFolder, $node];
5259
+    }
5260
+
5261
+    /**
5262
+     * @return list{Folder, File}
5263
+     */
5264
+    private function getNonSharedUserFile(): array {
5265
+        $node = $this->getMockBuilder(File::class)->getMock();
5266
+        $userFolder = $this->getMockBuilder(Folder::class)->getMock();
5267
+        $storage = $this->createMock(IStorage::class);
5268
+        $storage->method('instanceOfStorage')
5269
+            ->willReturnMap([
5270
+                ['OCA\Files_Sharing\External\Storage', false],
5271
+                ['OCA\Files_Sharing\SharedStorage', false],
5272
+            ]);
5273
+        $userFolder->method('getStorage')->willReturn($storage);
5274
+        $node->method('getStorage')->willReturn($storage);
5275
+        $node->method('getId')->willReturn(42);
5276
+        return [$userFolder, $node];
5277
+    }
5278
+
5279
+    public function testPopulateTags(): void {
5280
+        $tagger = $this->createMock(ITags::class);
5281
+        $this->tagManager->method('load')
5282
+            ->with('files')
5283
+            ->willReturn($tagger);
5284
+        $data = [
5285
+            ['file_source' => 10],
5286
+            ['file_source' => 22, 'foo' => 'bar'],
5287
+            ['file_source' => 42, 'x' => 'y'],
5288
+        ];
5289
+        $tags = [
5290
+            10 => ['tag3'],
5291
+            42 => ['tag1', 'tag2'],
5292
+        ];
5293
+        $tagger->method('getTagsForObjects')
5294
+            ->with([10, 22, 42])
5295
+            ->willReturn($tags);
5296
+
5297
+        $result = self::invokePrivate($this->ocs, 'populateTags', [$data]);
5298
+        $this->assertSame([
5299
+            ['file_source' => 10, 'tags' => ['tag3']],
5300
+            ['file_source' => 22, 'foo' => 'bar', 'tags' => []],
5301
+            ['file_source' => 42, 'x' => 'y', 'tags' => ['tag1', 'tag2']],
5302
+        ], $result);
5303
+    }
5304
+
5305
+    public static function trustedServerProvider(): array {
5306
+        return [
5307
+            'Trusted server' => [true, true],
5308
+            'Untrusted server' => [false, false],
5309
+        ];
5310
+    }
5311
+
5312
+    #[DataProvider('trustedServerProvider')]
5313
+    public function testFormatShareWithFederatedShare(bool $isKnownServer, bool $isTrusted): void {
5314
+        $nodeId = 12;
5315
+        $nodePath = '/test.txt';
5316
+
5317
+        $node = $this->createMock(File::class);
5318
+        $node->method('getId')->willReturn($nodeId);
5319
+        $node->method('getPath')->willReturn($nodePath);
5320
+        $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5321
+        $mountPoint = $this->createMock(IMountPoint::class);
5322
+        $mountPoint->method('getMountType')->willReturn('local');
5323
+        $node->method('getMountPoint')->willReturn($mountPoint);
5324
+        $node->method('getMimetype')->willReturn('text/plain');
5325
+        $storage = $this->createMock(IStorage::class);
5326
+        $storageCache = $this->createMock(ICache::class);
5327
+        $storageCache->method('getNumericStorageId')->willReturn(1);
5328
+        $storage->method('getCache')->willReturn($storageCache);
5329
+        $storage->method('getId')->willReturn('home::shareOwner');
5330
+        $node->method('getStorage')->willReturn($storage);
5331
+        $parent = $this->createMock(Folder::class);
5332
+        $parent->method('getId')->willReturn(2);
5333
+        $node->method('getParent')->willReturn($parent);
5334
+        $node->method('getSize')->willReturn(1234);
5335
+        $node->method('getMTime')->willReturn(1234567890);
5336
+
5337
+        $share = $this->createShare(
5338
+            1,
5339
+            IShare::TYPE_REMOTE,
5340
+            '[email protected]', // shared with
5341
+            '[email protected]',      // shared by
5342
+            'shareOwner',                 // share owner
5343
+            $node,
5344
+            Constants::PERMISSION_READ,
5345
+            time(),
5346
+            null,
5347
+            2,
5348
+            $nodePath,
5349
+            $nodeId
5350
+        );
5351
+
5352
+        $this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5353
+
5354
+        $this->rootFolder->method('getUserFolder')
5355
+            ->with($this->currentUser)
5356
+            ->willReturnSelf();
5357
+
5358
+        $this->rootFolder->method('getFirstNodeById')
5359
+            ->with($share->getNodeId())
5360
+            ->willReturn($node);
5361
+
5362
+        $this->rootFolder->method('getRelativePath')
5363
+            ->with($node->getPath())
5364
+            ->willReturnArgument(0);
5365
+
5366
+        $serverName = 'remoteserver.com';
5367
+        $this->trustedServers->method('isTrustedServer')
5368
+            ->with($serverName)
5369
+            ->willReturn($isKnownServer);
5370
+
5371
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5372
+
5373
+        $this->assertSame($isTrusted, $result['is_trusted_server']);
5374
+    }
5375
+
5376
+    public function testFormatShareWithFederatedShareWithAtInUsername(): void {
5377
+        $nodeId = 12;
5378
+        $nodePath = '/test.txt';
5379
+
5380
+        $node = $this->createMock(File::class);
5381
+        $node->method('getId')->willReturn($nodeId);
5382
+        $node->method('getPath')->willReturn($nodePath);
5383
+        $node->method('getInternalPath')->willReturn(ltrim($nodePath, '/'));
5384
+        $mountPoint = $this->createMock(IMountPoint::class);
5385
+        $mountPoint->method('getMountType')->willReturn('local');
5386
+        $node->method('getMountPoint')->willReturn($mountPoint);
5387
+        $node->method('getMimetype')->willReturn('text/plain');
5388
+        $storage = $this->createMock(IStorage::class);
5389
+        $storageCache = $this->createMock(ICache::class);
5390
+        $storageCache->method('getNumericStorageId')->willReturn(1);
5391
+        $storage->method('getCache')->willReturn($storageCache);
5392
+        $storage->method('getId')->willReturn('home::shareOwner');
5393
+        $node->method('getStorage')->willReturn($storage);
5394
+        $parent = $this->createMock(Folder::class);
5395
+        $parent->method('getId')->willReturn(2);
5396
+        $node->method('getParent')->willReturn($parent);
5397
+        $node->method('getSize')->willReturn(1234);
5398
+        $node->method('getMTime')->willReturn(1234567890);
5399
+
5400
+        $share = $this->createShare(
5401
+            1,
5402
+            IShare::TYPE_REMOTE,
5403
+            '[email protected]@remoteserver.com',
5404
+            '[email protected]',
5405
+            'shareOwner',
5406
+            $node,
5407
+            Constants::PERMISSION_READ,
5408
+            time(),
5409
+            null,
5410
+            2,
5411
+            $nodePath,
5412
+            $nodeId
5413
+        );
5414
+
5415
+        $this->previewManager->method('isAvailable')->with($node)->willReturn(false);
5416
+
5417
+        $this->rootFolder->method('getUserFolder')
5418
+            ->with($this->currentUser)
5419
+            ->willReturnSelf();
5420
+
5421
+        $this->rootFolder->method('getFirstNodeById')
5422
+            ->with($share->getNodeId())
5423
+            ->willReturn($node);
5424
+
5425
+        $this->rootFolder->method('getRelativePath')
5426
+            ->with($node->getPath())
5427
+            ->willReturnArgument(0);
5428
+
5429
+        $serverName = 'remoteserver.com';
5430
+        $this->trustedServers->method('isTrustedServer')
5431
+            ->with($serverName)
5432
+            ->willReturn(true);
5433
+
5434
+        $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]);
5435
+
5436
+        $this->assertTrue($result['is_trusted_server']);
5437
+    }
5438
+
5439
+    public function testOwnerCanAlwaysDownload(): void {
5440
+        $ocs = $this->mockFormatShare();
5441
+
5442
+        $share = $this->createMock(IShare::class);
5443
+        $node = $this->createMock(File::class);
5444
+        $userFolder = $this->createMock(Folder::class);
5445
+        $owner = $this->createMock(IUser::class);
5446
+
5447
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5448
+        $share->method('getNodeId')->willReturn(42);
5449
+        $node->method('getOwner')->willReturn($owner);
5450
+        $owner->method('getUID')->willReturn('sharedByUser');
5451
+
5452
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5453
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5454
+
5455
+        // Expect hideDownload to be set to false since owner can always download
5456
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5457
+
5458
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5459
+    }
5460
+
5461
+    public function testParentHideDownloadEnforcedOnChild(): void {
5462
+        $ocs = $this->mockFormatShare();
5463
+
5464
+        $share = $this->createMock(IShare::class);
5465
+        $node = $this->createMock(File::class);
5466
+        $userFolder = $this->createMock(Folder::class);
5467
+        $owner = $this->createMock(IUser::class);
5468
+        $storage = $this->createMock(SharedStorage::class);
5469
+        $originalShare = $this->createMock(IShare::class);
5470
+
5471
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5472
+        $share->method('getNodeId')->willReturn(42);
5473
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5474
+        $node->method('getOwner')->willReturn($owner);
5475
+        $owner->method('getUID')->willReturn('differentOwner');
5476
+        $node->method('getStorage')->willReturn($storage);
5477
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5478
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5479
+        $storage->method('getShare')->willReturn($originalShare);
5480
+        $originalShare->method('getHideDownload')->willReturn(true); // Parent hides download
5481
+        $originalShare->method('getAttributes')->willReturn(null);
5482
+
5483
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5484
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5485
+
5486
+        // Should be forced to hide download due to parent restriction
5487
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5488
+
5489
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5490
+    }
5491
+
5492
+    public function testUserCanHideWhenParentAllows(): void {
5493
+        $ocs = $this->mockFormatShare();
5494
+
5495
+        $share = $this->createMock(IShare::class);
5496
+        $node = $this->createMock(File::class);
5497
+        $userFolder = $this->createMock(Folder::class);
5498
+        $owner = $this->createMock(IUser::class);
5499
+        $storage = $this->createMock(SharedStorage::class);
5500
+        $originalShare = $this->createMock(IShare::class);
5501
+
5502
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5503
+        $share->method('getNodeId')->willReturn(42);
5504
+        $share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5505
+        $node->method('getOwner')->willReturn($owner);
5506
+        $owner->method('getUID')->willReturn('differentOwner');
5507
+        $node->method('getStorage')->willReturn($storage);
5508
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5509
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5510
+        $storage->method('getShare')->willReturn($originalShare);
5511
+        $originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5512
+        $originalShare->method('getAttributes')->willReturn(null);
5513
+
5514
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5515
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5516
+
5517
+        // Should respect user's choice to hide downloads
5518
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5519
+
5520
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5521
+    }
5522
+
5523
+    public function testParentDownloadAttributeInherited(): void {
5524
+        $ocs = $this->mockFormatShare();
5525
+
5526
+        $share = $this->createMock(IShare::class);
5527
+        $node = $this->createMock(File::class);
5528
+        $userFolder = $this->createMock(Folder::class);
5529
+        $owner = $this->createMock(IUser::class);
5530
+        $storage = $this->createMock(SharedStorage::class);
5531
+        $originalShare = $this->createMock(IShare::class);
5532
+        $attributes = $this->createMock(\OCP\Share\IAttributes::class);
5533
+        $shareAttributes = $this->createMock(\OCP\Share\IAttributes::class);
5534
+
5535
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5536
+        $share->method('getNodeId')->willReturn(42);
5537
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5538
+        $share->method('getAttributes')->willReturn($shareAttributes);
5539
+        $share->method('newAttributes')->willReturn($shareAttributes);
5540
+        $node->method('getOwner')->willReturn($owner);
5541
+        $owner->method('getUID')->willReturn('differentOwner');
5542
+        $node->method('getStorage')->willReturn($storage);
5543
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5544
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5545
+        $storage->method('getShare')->willReturn($originalShare);
5546
+        $originalShare->method('getHideDownload')->willReturn(false);
5547
+        $originalShare->method('getAttributes')->willReturn($attributes);
5548
+        $attributes->method('getAttribute')->with('permissions', 'download')->willReturn(false); // Parent forbids download
5549
+
5550
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5551
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5552
+
5553
+        // Should be forced to hide download and set download attribute to false
5554
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5555
+        $shareAttributes->expects($this->once())->method('setAttribute')->with('permissions', 'download', false);
5556
+        $share->expects($this->once())->method('setAttributes')->with($shareAttributes);
5557
+
5558
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5559
+    }
5560
+
5561
+    public function testFederatedStorageRespectsUserChoice(): void {
5562
+        $ocs = $this->mockFormatShare();
5563
+
5564
+        $share = $this->createMock(IShare::class);
5565
+        $node = $this->createMock(File::class);
5566
+        $userFolder = $this->createMock(Folder::class);
5567
+        $owner = $this->createMock(IUser::class);
5568
+        $storage = $this->createMock(\OCA\Files_Sharing\External\Storage::class);
5569
+
5570
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5571
+        $share->method('getNodeId')->willReturn(42);
5572
+        $share->method('getHideDownload')->willReturn(true); // User chooses to hide downloads
5573
+        $node->method('getOwner')->willReturn($owner);
5574
+        $owner->method('getUID')->willReturn('differentOwner');
5575
+        $node->method('getStorage')->willReturn($storage);
5576
+        $storage->method('instanceOfStorage')->willReturnMap([
5577
+            [SharedStorage::class, false],
5578
+            [\OCA\Files_Sharing\External\Storage::class, true]
5579
+        ]);
5580
+
5581
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5582
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5583
+
5584
+        // For federated storage, should respect user's choice
5585
+        $share->expects($this->once())->method('setHideDownload')->with(true);
5586
+
5587
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5588
+    }
5589
+
5590
+    public function testUserAllowsDownloadWhenParentPermits(): void {
5591
+        $ocs = $this->mockFormatShare();
5592
+
5593
+        $share = $this->createMock(IShare::class);
5594
+        $node = $this->createMock(File::class);
5595
+        $userFolder = $this->createMock(Folder::class);
5596
+        $owner = $this->createMock(IUser::class);
5597
+        $storage = $this->createMock(SharedStorage::class);
5598
+        $originalShare = $this->createMock(IShare::class);
5599
+
5600
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5601
+        $share->method('getNodeId')->willReturn(42);
5602
+        $share->method('getHideDownload')->willReturn(false); // User wants to allow downloads
5603
+        $node->method('getOwner')->willReturn($owner);
5604
+        $owner->method('getUID')->willReturn('differentOwner');
5605
+        $node->method('getStorage')->willReturn($storage);
5606
+        $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5607
+        $storage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($storage);
5608
+        $storage->method('getShare')->willReturn($originalShare);
5609
+        $originalShare->method('getHideDownload')->willReturn(false); // Parent allows download
5610
+        $originalShare->method('getAttributes')->willReturn(null);
5611
+
5612
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5613
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5614
+
5615
+        // Should allow downloads as both user and parent permit it
5616
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5617
+
5618
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5619
+    }
5620
+
5621
+    public function testWrapperStorageUnwrapped(): void {
5622
+        $ocs = $this->mockFormatShare();
5623
+
5624
+        $share = $this->createMock(IShare::class);
5625
+        $node = $this->createMock(File::class);
5626
+        $userFolder = $this->createMock(Folder::class);
5627
+        $owner = $this->createMock(IUser::class);
5628
+        $wrapperStorage = $this->createMock(Wrapper::class);
5629
+        $innerStorage = $this->createMock(SharedStorage::class);
5630
+        $originalShare = $this->createMock(IShare::class);
5631
+
5632
+        $share->method('getSharedBy')->willReturn('sharedByUser');
5633
+        $share->method('getNodeId')->willReturn(42);
5634
+        $share->method('getHideDownload')->willReturn(false);
5635
+        $node->method('getOwner')->willReturn($owner);
5636
+        $owner->method('getUID')->willReturn('differentOwner');
5637
+        $node->method('getStorage')->willReturn($wrapperStorage);
5638
+        $wrapperStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
5639
+        $wrapperStorage->method('getInstanceOfStorage')->with(SharedStorage::class)->willReturn($innerStorage);
5640
+        $innerStorage->method('getShare')->willReturn($originalShare);
5641
+        $originalShare->method('getHideDownload')->willReturn(false);
5642
+        $originalShare->method('getAttributes')->willReturn(null);
5643
+
5644
+        $userFolder->method('getById')->with(42)->willReturn([$node]);
5645
+        $this->rootFolder->method('getUserFolder')->with('sharedByUser')->willReturn($userFolder);
5646
+
5647
+        $share->expects($this->once())->method('setHideDownload')->with(false);
5648
+
5649
+        $this->invokePrivate($ocs, 'checkInheritedAttributes', [$share]);
5650
+    }
5651 5651
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareAPIController.php 1 patch
Indentation   +2209 added lines, -2209 removed lines patch added patch discarded remove patch
@@ -77,2216 +77,2216 @@
 block discarded – undo
77 77
  */
78 78
 class ShareAPIController extends OCSController {
79 79
 
80
-	private ?Node $lockedNode = null;
81
-	private array $trustedServerCache = [];
82
-
83
-	/**
84
-	 * Share20OCS constructor.
85
-	 */
86
-	public function __construct(
87
-		string $appName,
88
-		IRequest $request,
89
-		private IManager $shareManager,
90
-		private IGroupManager $groupManager,
91
-		private IUserManager $userManager,
92
-		private IRootFolder $rootFolder,
93
-		private IURLGenerator $urlGenerator,
94
-		private IL10N $l,
95
-		private IConfig $config,
96
-		private IAppConfig $appConfig,
97
-		private IAppManager $appManager,
98
-		private ContainerInterface $serverContainer,
99
-		private IUserStatusManager $userStatusManager,
100
-		private IPreview $previewManager,
101
-		private IDateTimeZone $dateTimeZone,
102
-		private LoggerInterface $logger,
103
-		private IProviderFactory $factory,
104
-		private IMailer $mailer,
105
-		private ITagManager $tagManager,
106
-		private IEmailValidator $emailValidator,
107
-		private ?TrustedServers $trustedServers,
108
-		private ?string $userId = null,
109
-	) {
110
-		parent::__construct($appName, $request);
111
-	}
112
-
113
-	/**
114
-	 * Convert an IShare to an array for OCS output
115
-	 *
116
-	 * @param IShare $share
117
-	 * @param Node|null $recipientNode
118
-	 * @return Files_SharingShare
119
-	 * @throws NotFoundException In case the node can't be resolved.
120
-	 *
121
-	 * @suppress PhanUndeclaredClassMethod
122
-	 */
123
-	protected function formatShare(IShare $share, ?Node $recipientNode = null): array {
124
-		$sharedBy = $this->userManager->get($share->getSharedBy());
125
-		$shareOwner = $this->userManager->get($share->getShareOwner());
126
-
127
-		$isOwnShare = false;
128
-		if ($shareOwner !== null) {
129
-			$isOwnShare = $shareOwner->getUID() === $this->userId;
130
-		}
131
-
132
-		$result = [
133
-			'id' => $share->getId(),
134
-			'share_type' => $share->getShareType(),
135
-			'uid_owner' => $share->getSharedBy(),
136
-			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
137
-			// recipient permissions
138
-			'permissions' => $share->getPermissions(),
139
-			// current user permissions on this share
140
-			'can_edit' => $this->canEditShare($share),
141
-			'can_delete' => $this->canDeleteShare($share),
142
-			'stime' => $share->getShareTime()->getTimestamp(),
143
-			'parent' => null,
144
-			'expiration' => null,
145
-			'token' => null,
146
-			'uid_file_owner' => $share->getShareOwner(),
147
-			'note' => $share->getNote(),
148
-			'label' => $share->getLabel(),
149
-			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
150
-		];
151
-
152
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
153
-		if ($recipientNode) {
154
-			$node = $recipientNode;
155
-		} else {
156
-			$node = $userFolder->getFirstNodeById($share->getNodeId());
157
-			if (!$node) {
158
-				// fallback to guessing the path
159
-				$node = $userFolder->get($share->getTarget());
160
-				if ($node === null || $share->getTarget() === '') {
161
-					throw new NotFoundException();
162
-				}
163
-			}
164
-		}
165
-
166
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
167
-		if ($node instanceof Folder) {
168
-			$result['item_type'] = 'folder';
169
-		} else {
170
-			$result['item_type'] = 'file';
171
-		}
172
-
173
-		// Get the original node permission if the share owner is the current user
174
-		if ($isOwnShare) {
175
-			$result['item_permissions'] = $node->getPermissions();
176
-		}
177
-
178
-		// If we're on the recipient side, the node permissions
179
-		// are bound to the share permissions. So we need to
180
-		// adjust the permissions to the share permissions if necessary.
181
-		if (!$isOwnShare) {
182
-			$result['item_permissions'] = $share->getPermissions();
183
-
184
-			// For some reason, single files share are forbidden to have the delete permission
185
-			// since we have custom methods to check those, let's adjust straight away.
186
-			// DAV permissions does not have that issue though.
187
-			if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
188
-				$result['item_permissions'] |= Constants::PERMISSION_DELETE;
189
-			}
190
-			if ($this->canEditShare($share)) {
191
-				$result['item_permissions'] |= Constants::PERMISSION_UPDATE;
192
-			}
193
-		}
194
-
195
-		// See MOUNT_ROOT_PROPERTYNAME dav property
196
-		$result['is-mount-root'] = $node->getInternalPath() === '';
197
-		$result['mount-type'] = $node->getMountPoint()->getMountType();
198
-
199
-		$result['mimetype'] = $node->getMimetype();
200
-		$result['has_preview'] = $this->previewManager->isAvailable($node);
201
-		$result['storage_id'] = $node->getStorage()->getId();
202
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
203
-		$result['item_source'] = $node->getId();
204
-		$result['file_source'] = $node->getId();
205
-		$result['file_parent'] = $node->getParent()->getId();
206
-		$result['file_target'] = $share->getTarget();
207
-		$result['item_size'] = $node->getSize();
208
-		$result['item_mtime'] = $node->getMTime();
209
-
210
-		if ($this->trustedServers !== null && in_array($share->getShareType(), [IShare::TYPE_REMOTE, IShare::TYPE_REMOTE_GROUP], true)) {
211
-			$result['is_trusted_server'] = false;
212
-			$sharedWith = $share->getSharedWith();
213
-			$remoteIdentifier = is_string($sharedWith) ? strrchr($sharedWith, '@') : false;
214
-			if ($remoteIdentifier !== false) {
215
-				$remote = substr($remoteIdentifier, 1);
216
-
217
-				if (isset($this->trustedServerCache[$remote])) {
218
-					$result['is_trusted_server'] = $this->trustedServerCache[$remote];
219
-				} else {
220
-					try {
221
-						$isTrusted = $this->trustedServers->isTrustedServer($remote);
222
-						$this->trustedServerCache[$remote] = $isTrusted;
223
-						$result['is_trusted_server'] = $isTrusted;
224
-					} catch (\Exception $e) {
225
-						// Server not found or other issue, we consider it not trusted
226
-						$this->trustedServerCache[$remote] = false;
227
-						$this->logger->error(
228
-							'Error checking if remote server is trusted (treating as untrusted): ' . $e->getMessage(),
229
-							['exception' => $e]
230
-						);
231
-					}
232
-				}
233
-			}
234
-		}
235
-
236
-		$expiration = $share->getExpirationDate();
237
-		if ($expiration !== null) {
238
-			$expiration->setTimezone($this->dateTimeZone->getTimeZone());
239
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
240
-		}
241
-
242
-		if ($share->getShareType() === IShare::TYPE_USER) {
243
-			$sharedWith = $this->userManager->get($share->getSharedWith());
244
-			$result['share_with'] = $share->getSharedWith();
245
-			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
246
-			$result['share_with_displayname_unique'] = $sharedWith !== null ? (
247
-				!empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
248
-			) : $share->getSharedWith();
249
-
250
-			$userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
251
-			$userStatus = array_shift($userStatuses);
252
-			if ($userStatus) {
253
-				$result['status'] = [
254
-					'status' => $userStatus->getStatus(),
255
-					'message' => $userStatus->getMessage(),
256
-					'icon' => $userStatus->getIcon(),
257
-					'clearAt' => $userStatus->getClearAt()
258
-						? (int)$userStatus->getClearAt()->format('U')
259
-						: null,
260
-				];
261
-			}
262
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
263
-			$group = $this->groupManager->get($share->getSharedWith());
264
-			$result['share_with'] = $share->getSharedWith();
265
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
266
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
267
-
268
-			// "share_with" and "share_with_displayname" for passwords of link
269
-			// shares was deprecated in Nextcloud 15, use "password" instead.
270
-			$result['share_with'] = $share->getPassword();
271
-			$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
272
-
273
-			$result['password'] = $share->getPassword();
274
-
275
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
276
-
277
-			$result['token'] = $share->getToken();
278
-			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
279
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
280
-			$result['share_with'] = $share->getSharedWith();
281
-			$result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
282
-			$result['token'] = $share->getToken();
283
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
284
-			$result['share_with'] = $share->getSharedWith();
285
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
286
-			$result['token'] = $share->getToken();
287
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
288
-			$result['share_with'] = $share->getSharedWith();
289
-			$result['password'] = $share->getPassword();
290
-			$result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
291
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
292
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
293
-			$result['token'] = $share->getToken();
294
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
295
-			// getSharedWith() returns either "name (type, owner)" or
296
-			// "name (type, owner) [id]", depending on the Teams app version.
297
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
298
-
299
-			$result['share_with_displayname'] = $share->getSharedWithDisplayName();
300
-			if (empty($result['share_with_displayname'])) {
301
-				$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
302
-				$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
303
-			}
304
-
305
-			$result['share_with_avatar'] = $share->getSharedWithAvatar();
306
-
307
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
308
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
309
-			if ($shareWithLength === false) {
310
-				$result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
311
-			} else {
312
-				$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
313
-			}
314
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
315
-			$result['share_with'] = $share->getSharedWith();
316
-			$result['share_with_displayname'] = '';
317
-
318
-			try {
319
-				/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
320
-				$roomShare = $this->getRoomShareHelper()->formatShare($share);
321
-				$result = array_merge($result, $roomShare);
322
-			} catch (ContainerExceptionInterface $e) {
323
-			}
324
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
325
-			$result['share_with'] = $share->getSharedWith();
326
-			$result['share_with_displayname'] = '';
327
-
328
-			try {
329
-				/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
330
-				$deckShare = $this->getDeckShareHelper()->formatShare($share);
331
-				$result = array_merge($result, $deckShare);
332
-			} catch (ContainerExceptionInterface $e) {
333
-			}
334
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
335
-			$result['share_with'] = $share->getSharedWith();
336
-			$result['share_with_displayname'] = '';
337
-
338
-			try {
339
-				/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
340
-				$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
341
-				$result = array_merge($result, $scienceMeshShare);
342
-			} catch (ContainerExceptionInterface $e) {
343
-			}
344
-		}
345
-
346
-
347
-		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
348
-		$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
349
-
350
-		$result['attributes'] = null;
351
-		if ($attributes = $share->getAttributes()) {
352
-			$result['attributes'] = (string)\json_encode($attributes->toArray());
353
-		}
354
-
355
-		return $result;
356
-	}
357
-
358
-	/**
359
-	 * Check if one of the users address books knows the exact property, if
360
-	 * not we return the full name.
361
-	 *
362
-	 * @param string $query
363
-	 * @param string $property
364
-	 * @return string
365
-	 */
366
-	private function getDisplayNameFromAddressBook(string $query, string $property): string {
367
-		// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
368
-		try {
369
-			$result = Server::get(\OCP\Contacts\IManager::class)->search($query, [$property], [
370
-				'limit' => 1,
371
-				'enumeration' => false,
372
-				'strict_search' => true,
373
-			]);
374
-		} catch (Exception $e) {
375
-			$this->logger->error(
376
-				$e->getMessage(),
377
-				['exception' => $e]
378
-			);
379
-			return $query;
380
-		}
381
-
382
-		foreach ($result as $r) {
383
-			foreach ($r[$property] as $value) {
384
-				if ($value === $query && $r['FN']) {
385
-					return $r['FN'];
386
-				}
387
-			}
388
-		}
389
-
390
-		return $query;
391
-	}
392
-
393
-
394
-	/**
395
-	 * @param list<Files_SharingShare> $shares
396
-	 * @param array<string, string>|null $updatedDisplayName
397
-	 *
398
-	 * @return list<Files_SharingShare>
399
-	 */
400
-	private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
401
-		$userIds = $updated = [];
402
-		foreach ($shares as $share) {
403
-			// share is federated and share have no display name yet
404
-			if ($share['share_type'] === IShare::TYPE_REMOTE
405
-				&& ($share['share_with'] ?? '') !== ''
406
-				&& ($share['share_with_displayname'] ?? '') === '') {
407
-				$userIds[] = $userId = $share['share_with'];
408
-
409
-				if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
410
-					$share['share_with_displayname'] = $updatedDisplayName[$userId];
411
-				}
412
-			}
413
-
414
-			// prepping userIds with displayName to be updated
415
-			$updated[] = $share;
416
-		}
417
-
418
-		// if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
419
-		if ($updatedDisplayName !== null) {
420
-			return $updated;
421
-		}
422
-
423
-		// get displayName for the generated list of userId with no displayName
424
-		$displayNames = $this->retrieveFederatedDisplayName($userIds);
425
-
426
-		// if no displayName are updated, we exit
427
-		if (empty($displayNames)) {
428
-			return $updated;
429
-		}
430
-
431
-		// let's fix missing display name and returns all shares
432
-		return $this->fixMissingDisplayName($shares, $displayNames);
433
-	}
434
-
435
-
436
-	/**
437
-	 * get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
438
-	 * returns an array with userIds as keys and displayName as values.
439
-	 *
440
-	 * @param array $userIds
441
-	 * @param bool $cacheOnly - do not reach LUS, get data from cache.
442
-	 *
443
-	 * @return array
444
-	 * @throws ContainerExceptionInterface
445
-	 */
446
-	private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
447
-		// check if gss is enabled and available
448
-		if (count($userIds) === 0
449
-			|| !$this->appManager->isEnabledForAnyone('globalsiteselector')
450
-			|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
451
-			return [];
452
-		}
453
-
454
-		try {
455
-			$slaveService = Server::get(SlaveService::class);
456
-		} catch (\Throwable $e) {
457
-			$this->logger->error(
458
-				$e->getMessage(),
459
-				['exception' => $e]
460
-			);
461
-			return [];
462
-		}
463
-
464
-		return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
465
-	}
466
-
467
-
468
-	/**
469
-	 * retrieve displayName from cache if available (should be used on federated shares)
470
-	 * if not available in cache/lus, try for get from address-book, else returns empty string.
471
-	 *
472
-	 * @param string $userId
473
-	 * @param bool $cacheOnly if true will not reach the lus but will only get data from cache
474
-	 *
475
-	 * @return string
476
-	 */
477
-	private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
478
-		$details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
479
-		if (array_key_exists($userId, $details)) {
480
-			return $details[$userId];
481
-		}
482
-
483
-		$displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
484
-		return ($displayName === $userId) ? '' : $displayName;
485
-	}
486
-
487
-
488
-
489
-	/**
490
-	 * Get a specific share by id
491
-	 *
492
-	 * @param string $id ID of the share
493
-	 * @param bool $include_tags Include tags in the share
494
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
495
-	 * @throws OCSNotFoundException Share not found
496
-	 *
497
-	 * 200: Share returned
498
-	 */
499
-	#[NoAdminRequired]
500
-	public function getShare(string $id, bool $include_tags = false): DataResponse {
501
-		try {
502
-			$share = $this->getShareById($id);
503
-		} catch (ShareNotFound $e) {
504
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
505
-		}
506
-
507
-		try {
508
-			if ($this->canAccessShare($share)) {
509
-				$share = $this->formatShare($share);
510
-
511
-				if ($include_tags) {
512
-					$share = $this->populateTags([$share]);
513
-				} else {
514
-					$share = [$share];
515
-				}
516
-
517
-				return new DataResponse($share);
518
-			}
519
-		} catch (NotFoundException $e) {
520
-			// Fall through
521
-		}
522
-
523
-		throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
524
-	}
525
-
526
-	/**
527
-	 * Delete a share
528
-	 *
529
-	 * @param string $id ID of the share
530
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
531
-	 * @throws OCSNotFoundException Share not found
532
-	 * @throws OCSForbiddenException Missing permissions to delete the share
533
-	 *
534
-	 * 200: Share deleted successfully
535
-	 */
536
-	#[NoAdminRequired]
537
-	public function deleteShare(string $id): DataResponse {
538
-		try {
539
-			$share = $this->getShareById($id);
540
-		} catch (ShareNotFound $e) {
541
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
542
-		}
543
-
544
-		try {
545
-			$this->lock($share->getNode());
546
-		} catch (LockedException $e) {
547
-			throw new OCSNotFoundException($this->l->t('Could not delete share'));
548
-		}
549
-
550
-		if (!$this->canAccessShare($share)) {
551
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
552
-		}
553
-
554
-		// if it's a group share or a room share
555
-		// we don't delete the share, but only the
556
-		// mount point. Allowing it to be restored
557
-		// from the deleted shares
558
-		if ($this->canDeleteShareFromSelf($share)) {
559
-			$this->shareManager->deleteFromSelf($share, $this->userId);
560
-		} else {
561
-			if (!$this->canDeleteShare($share)) {
562
-				throw new OCSForbiddenException($this->l->t('Could not delete share'));
563
-			}
564
-
565
-			$this->shareManager->deleteShare($share);
566
-		}
567
-
568
-		return new DataResponse();
569
-	}
570
-
571
-	/**
572
-	 * Create a share
573
-	 *
574
-	 * @param string|null $path Path of the share
575
-	 * @param int|null $permissions Permissions for the share
576
-	 * @param int $shareType Type of the share
577
-	 * @param ?string $shareWith The entity this should be shared with
578
-	 * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
579
-	 * @param string $password Password for the share
580
-	 * @param string|null $sendPasswordByTalk Send the password for the share over Talk
581
-	 * @param ?string $expireDate The expiry date of the share in the user's timezone at 00:00.
582
-	 *                            If $expireDate is not supplied or set to `null`, the system default will be used.
583
-	 * @param string $note Note for the share
584
-	 * @param string $label Label for the share (only used in link and email)
585
-	 * @param string|null $attributes Additional attributes for the share
586
-	 * @param 'false'|'true'|null $sendMail Send a mail to the recipient
587
-	 *
588
-	 * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
589
-	 * @throws OCSBadRequestException Unknown share type
590
-	 * @throws OCSException
591
-	 * @throws OCSForbiddenException Creating the share is not allowed
592
-	 * @throws OCSNotFoundException Creating the share failed
593
-	 * @suppress PhanUndeclaredClassMethod
594
-	 *
595
-	 * 200: Share created
596
-	 */
597
-	#[NoAdminRequired]
598
-	#[UserRateLimit(limit: 20, period: 600)]
599
-	public function createShare(
600
-		?string $path = null,
601
-		?int $permissions = null,
602
-		int $shareType = -1,
603
-		?string $shareWith = null,
604
-		?string $publicUpload = null,
605
-		string $password = '',
606
-		?string $sendPasswordByTalk = null,
607
-		?string $expireDate = null,
608
-		string $note = '',
609
-		string $label = '',
610
-		?string $attributes = null,
611
-		?string $sendMail = null,
612
-	): DataResponse {
613
-		assert($this->userId !== null);
614
-
615
-		$share = $this->shareManager->newShare();
616
-		$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
617
-
618
-		// Verify path
619
-		if ($path === null) {
620
-			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
621
-		}
622
-
623
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
624
-		try {
625
-			/** @var \OC\Files\Node\Node $node */
626
-			$node = $userFolder->get($path);
627
-		} catch (NotFoundException $e) {
628
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
629
-		}
630
-
631
-		// a user can have access to a file through different paths, with differing permissions
632
-		// combine all permissions to determine if the user can share this file
633
-		$nodes = $userFolder->getById($node->getId());
634
-		foreach ($nodes as $nodeById) {
635
-			/** @var FileInfo $fileInfo */
636
-			$fileInfo = $node->getFileInfo();
637
-			$fileInfo['permissions'] |= $nodeById->getPermissions();
638
-		}
639
-
640
-		$share->setNode($node);
641
-
642
-		try {
643
-			$this->lock($share->getNode());
644
-		} catch (LockedException $e) {
645
-			throw new OCSNotFoundException($this->l->t('Could not create share'));
646
-		}
647
-
648
-		// Set permissions
649
-		if ($shareType === IShare::TYPE_LINK || $shareType === IShare::TYPE_EMAIL) {
650
-			$permissions = $this->getLinkSharePermissions($permissions, $hasPublicUpload);
651
-			$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
652
-		} else {
653
-			// Use default permissions only for non-link shares to keep legacy behavior
654
-			if ($permissions === null) {
655
-				$permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
656
-			}
657
-			// Non-link shares always require read permissions (link shares could be file drop)
658
-			$permissions |= Constants::PERMISSION_READ;
659
-		}
660
-
661
-		// For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
662
-		if ($node instanceof File) {
663
-			// if this is a single file share we remove the DELETE and CREATE permissions
664
-			$permissions = $permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE);
665
-		}
666
-
667
-		/**
668
-		 * Hack for https://github.com/owncloud/core/issues/22587
669
-		 * We check the permissions via webdav. But the permissions of the mount point
670
-		 * do not equal the share permissions. Here we fix that for federated mounts.
671
-		 */
672
-		if ($node->getStorage()->instanceOfStorage(Storage::class)) {
673
-			$permissions &= ~($permissions & ~$node->getPermissions());
674
-		}
675
-
676
-		if ($attributes !== null) {
677
-			$share = $this->setShareAttributes($share, $attributes);
678
-		}
679
-
680
-		// Expire date checks
681
-		// Normally, null means no expiration date but we still set the default for backwards compatibility
682
-		// If the client sends an empty string, we set noExpirationDate to true
683
-		if ($expireDate !== null) {
684
-			if ($expireDate !== '') {
685
-				try {
686
-					$expireDateTime = $this->parseDate($expireDate);
687
-					$share->setExpirationDate($expireDateTime);
688
-				} catch (\Exception $e) {
689
-					throw new OCSNotFoundException($e->getMessage(), $e);
690
-				}
691
-			} else {
692
-				// Client sent empty string for expire date.
693
-				// Set noExpirationDate to true so overwrite is prevented.
694
-				$share->setNoExpirationDate(true);
695
-			}
696
-		}
697
-
698
-		$share->setSharedBy($this->userId);
699
-
700
-		// Handle mail send
701
-		if (is_null($sendMail)) {
702
-			$allowSendMail = $this->config->getSystemValueBool('sharing.enable_share_mail', true);
703
-			if ($allowSendMail !== true || $shareType === IShare::TYPE_EMAIL) {
704
-				// Define a default behavior when sendMail is not provided
705
-				// For email shares with a valid recipient, the default is to send the mail
706
-				// For all other share types, the default is to not send the mail
707
-				$allowSendMail = ($shareType === IShare::TYPE_EMAIL && $shareWith !== null && $shareWith !== '');
708
-			}
709
-			$share->setMailSend($allowSendMail);
710
-		} else {
711
-			$share->setMailSend($sendMail === 'true');
712
-		}
713
-
714
-		if ($shareType === IShare::TYPE_USER) {
715
-			// Valid user is required to share
716
-			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
717
-				throw new OCSNotFoundException($this->l->t('Please specify a valid account to share with'));
718
-			}
719
-			$share->setSharedWith($shareWith);
720
-			$share->setPermissions($permissions);
721
-		} elseif ($shareType === IShare::TYPE_GROUP) {
722
-			if (!$this->shareManager->allowGroupSharing()) {
723
-				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
724
-			}
725
-
726
-			// Valid group is required to share
727
-			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
728
-				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
729
-			}
730
-			$share->setSharedWith($shareWith);
731
-			$share->setPermissions($permissions);
732
-		} elseif ($shareType === IShare::TYPE_LINK
733
-			|| $shareType === IShare::TYPE_EMAIL) {
734
-
735
-			// Can we even share links?
736
-			if (!$this->shareManager->shareApiAllowLinks()) {
737
-				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
738
-			}
739
-
740
-			$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
741
-			$share->setPermissions($permissions);
742
-
743
-			// Set password
744
-			if ($password !== '') {
745
-				$share->setPassword($password);
746
-			}
747
-
748
-			// Only share by mail have a recipient
749
-			if (is_string($shareWith) && $shareType === IShare::TYPE_EMAIL) {
750
-				// If sending a mail have been requested, validate the mail address
751
-				if ($share->getMailSend() && !$this->emailValidator->isValid($shareWith)) {
752
-					throw new OCSNotFoundException($this->l->t('Please specify a valid email address'));
753
-				}
754
-				$share->setSharedWith($shareWith);
755
-			}
756
-
757
-			// If we have a label, use it
758
-			if ($label !== '') {
759
-				if (strlen($label) > 255) {
760
-					throw new OCSBadRequestException('Maximum label length is 255');
761
-				}
762
-				$share->setLabel($label);
763
-			}
764
-
765
-			if ($sendPasswordByTalk === 'true') {
766
-				if (!$this->appManager->isEnabledForUser('spreed')) {
767
-					throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$node->getPath()]));
768
-				}
769
-
770
-				$share->setSendPasswordByTalk(true);
771
-			}
772
-		} elseif ($shareType === IShare::TYPE_REMOTE) {
773
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
774
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
775
-			}
776
-
777
-			if ($shareWith === null) {
778
-				throw new OCSNotFoundException($this->l->t('Please specify a valid federated account ID'));
779
-			}
780
-
781
-			$share->setSharedWith($shareWith);
782
-			$share->setPermissions($permissions);
783
-			$share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
784
-		} elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
785
-			if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
786
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
787
-			}
788
-
789
-			if ($shareWith === null) {
790
-				throw new OCSNotFoundException($this->l->t('Please specify a valid federated group ID'));
791
-			}
792
-
793
-			$share->setSharedWith($shareWith);
794
-			$share->setPermissions($permissions);
795
-		} elseif ($shareType === IShare::TYPE_CIRCLE) {
796
-			if (!Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
797
-				throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
798
-			}
799
-
800
-			$circle = Circles::detailsCircle($shareWith);
801
-
802
-			// Valid team is required to share
803
-			if ($circle === null) {
804
-				throw new OCSNotFoundException($this->l->t('Please specify a valid team'));
805
-			}
806
-			$share->setSharedWith($shareWith);
807
-			$share->setPermissions($permissions);
808
-		} elseif ($shareType === IShare::TYPE_ROOM) {
809
-			try {
810
-				$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
811
-			} catch (ContainerExceptionInterface $e) {
812
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
813
-			}
814
-		} elseif ($shareType === IShare::TYPE_DECK) {
815
-			try {
816
-				$this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
817
-			} catch (ContainerExceptionInterface $e) {
818
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
819
-			}
820
-		} elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
821
-			try {
822
-				$this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
823
-			} catch (ContainerExceptionInterface $e) {
824
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
825
-			}
826
-		} else {
827
-			throw new OCSBadRequestException($this->l->t('Unknown share type'));
828
-		}
829
-
830
-		$share->setShareType($shareType);
831
-		$this->checkInheritedAttributes($share);
832
-
833
-		if ($note !== '') {
834
-			$share->setNote($note);
835
-		}
836
-
837
-		try {
838
-			$share = $this->shareManager->createShare($share);
839
-		} catch (HintException $e) {
840
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
841
-			throw new OCSException($e->getHint(), $code);
842
-		} catch (GenericShareException|\InvalidArgumentException $e) {
843
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
844
-			throw new OCSForbiddenException($e->getMessage(), $e);
845
-		} catch (\Exception $e) {
846
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
847
-			throw new OCSForbiddenException('Failed to create share.', $e);
848
-		}
849
-
850
-		$output = $this->formatShare($share);
851
-
852
-		return new DataResponse($output);
853
-	}
854
-
855
-	/**
856
-	 * @param null|Node $node
857
-	 * @param boolean $includeTags
858
-	 *
859
-	 * @return list<Files_SharingShare>
860
-	 */
861
-	private function getSharedWithMe($node, bool $includeTags): array {
862
-		$userShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_USER, $node, -1, 0);
863
-		$groupShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_GROUP, $node, -1, 0);
864
-		$circleShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_CIRCLE, $node, -1, 0);
865
-		$roomShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_ROOM, $node, -1, 0);
866
-		$deckShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_DECK, $node, -1, 0);
867
-		$sciencemeshShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, $node, -1, 0);
868
-
869
-		$shares = array_merge($userShares, $groupShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares);
870
-
871
-		$filteredShares = array_filter($shares, function (IShare $share) {
872
-			return $share->getShareOwner() !== $this->userId && $share->getSharedBy() !== $this->userId;
873
-		});
874
-
875
-		$formatted = [];
876
-		foreach ($filteredShares as $share) {
877
-			if ($this->canAccessShare($share)) {
878
-				try {
879
-					$formatted[] = $this->formatShare($share);
880
-				} catch (NotFoundException $e) {
881
-					// Ignore this share
882
-				}
883
-			}
884
-		}
885
-
886
-		if ($includeTags) {
887
-			$formatted = $this->populateTags($formatted);
888
-		}
889
-
890
-		return $formatted;
891
-	}
892
-
893
-	/**
894
-	 * @param Node $folder
895
-	 *
896
-	 * @return list<Files_SharingShare>
897
-	 * @throws OCSBadRequestException
898
-	 * @throws NotFoundException
899
-	 */
900
-	private function getSharesInDir(Node $folder): array {
901
-		if (!($folder instanceof Folder)) {
902
-			throw new OCSBadRequestException($this->l->t('Not a directory'));
903
-		}
904
-
905
-		$nodes = $folder->getDirectoryListing();
906
-
907
-		/** @var IShare[] $shares */
908
-		$shares = array_reduce($nodes, function ($carry, $node) {
909
-			$carry = array_merge($carry, $this->getAllShares($node, true));
910
-			return $carry;
911
-		}, []);
912
-
913
-		// filter out duplicate shares
914
-		$known = [];
915
-
916
-		$formatted = $miniFormatted = [];
917
-		$resharingRight = false;
918
-		$known = [];
919
-		foreach ($shares as $share) {
920
-			if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->userId) {
921
-				continue;
922
-			}
923
-
924
-			try {
925
-				$format = $this->formatShare($share);
926
-
927
-				$known[] = $share->getId();
928
-				$formatted[] = $format;
929
-				if ($share->getSharedBy() === $this->userId) {
930
-					$miniFormatted[] = $format;
931
-				}
932
-				if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $folder)) {
933
-					$resharingRight = true;
934
-				}
935
-			} catch (\Exception $e) {
936
-				//Ignore this share
937
-			}
938
-		}
939
-
940
-		if (!$resharingRight) {
941
-			$formatted = $miniFormatted;
942
-		}
943
-
944
-		return $formatted;
945
-	}
946
-
947
-	/**
948
-	 * Get shares of the current user
949
-	 *
950
-	 * @param string $shared_with_me Only get shares with the current user
951
-	 * @param string $reshares Only get shares by the current user and reshares
952
-	 * @param string $subfiles Only get all shares in a folder
953
-	 * @param string $path Get shares for a specific path
954
-	 * @param string $include_tags Include tags in the share
955
-	 *
956
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
957
-	 * @throws OCSNotFoundException The folder was not found or is inaccessible
958
-	 *
959
-	 * 200: Shares returned
960
-	 */
961
-	#[NoAdminRequired]
962
-	public function getShares(
963
-		string $shared_with_me = 'false',
964
-		string $reshares = 'false',
965
-		string $subfiles = 'false',
966
-		string $path = '',
967
-		string $include_tags = 'false',
968
-	): DataResponse {
969
-		$node = null;
970
-		if ($path !== '') {
971
-			$userFolder = $this->rootFolder->getUserFolder($this->userId);
972
-			try {
973
-				$node = $userFolder->get($path);
974
-				$this->lock($node);
975
-			} catch (NotFoundException $e) {
976
-				throw new OCSNotFoundException(
977
-					$this->l->t('Wrong path, file/folder does not exist')
978
-				);
979
-			} catch (LockedException $e) {
980
-				throw new OCSNotFoundException($this->l->t('Could not lock node'));
981
-			}
982
-		}
983
-
984
-		$shares = $this->getFormattedShares(
985
-			$this->userId,
986
-			$node,
987
-			($shared_with_me === 'true'),
988
-			($reshares === 'true'),
989
-			($subfiles === 'true'),
990
-			($include_tags === 'true')
991
-		);
992
-
993
-		return new DataResponse($shares);
994
-	}
995
-
996
-	private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicUpload): int {
997
-		$permissions = $permissions ?? Constants::PERMISSION_READ;
998
-
999
-		// Legacy option handling
1000
-		if ($legacyPublicUpload !== null) {
1001
-			$permissions = $legacyPublicUpload
1002
-				? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1003
-				: Constants::PERMISSION_READ;
1004
-		}
1005
-
1006
-		if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
1007
-			&& $this->shareManager->outgoingServer2ServerSharesAllowed()
1008
-			&& $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES)) {
1009
-			$permissions |= Constants::PERMISSION_SHARE;
1010
-		}
1011
-
1012
-		return $permissions;
1013
-	}
1014
-
1015
-	/**
1016
-	 * Helper to check for legacy "publicUpload" handling.
1017
-	 * If the value is set to `true` or `false` then true or false are returned.
1018
-	 * Otherwise null is returned to indicate that the option was not (or wrong) set.
1019
-	 *
1020
-	 * @param null|string $legacyPublicUpload The value of `publicUpload`
1021
-	 */
1022
-	private function getLegacyPublicUpload(?string $legacyPublicUpload): ?bool {
1023
-		if ($legacyPublicUpload === 'true') {
1024
-			return true;
1025
-		} elseif ($legacyPublicUpload === 'false') {
1026
-			return false;
1027
-		}
1028
-		// Not set at all
1029
-		return null;
1030
-	}
1031
-
1032
-	/**
1033
-	 * For link and email shares validate that only allowed combinations are set.
1034
-	 *
1035
-	 * @throw OCSBadRequestException If permission combination is invalid.
1036
-	 * @throw OCSForbiddenException If public upload was forbidden by the administrator.
1037
-	 */
1038
-	private function validateLinkSharePermissions(Node $node, int $permissions, ?bool $legacyPublicUpload): void {
1039
-		if ($legacyPublicUpload && ($node instanceof File)) {
1040
-			throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1041
-		}
1042
-
1043
-		// We need at least READ or CREATE (file drop)
1044
-		if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1045
-			&& !$this->hasPermission($permissions, Constants::PERMISSION_CREATE)) {
1046
-			throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1047
-		}
1048
-
1049
-		// UPDATE and DELETE require a READ permission
1050
-		if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1051
-			&& ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
1052
-			throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1053
-		}
1054
-
1055
-		// Check if public uploading was disabled
1056
-		if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1057
-			&& !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1058
-			throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1059
-		}
1060
-	}
1061
-
1062
-	/**
1063
-	 * @param string $viewer
1064
-	 * @param Node $node
1065
-	 * @param bool $sharedWithMe
1066
-	 * @param bool $reShares
1067
-	 * @param bool $subFiles
1068
-	 * @param bool $includeTags
1069
-	 *
1070
-	 * @return list<Files_SharingShare>
1071
-	 * @throws NotFoundException
1072
-	 * @throws OCSBadRequestException
1073
-	 */
1074
-	private function getFormattedShares(
1075
-		string $viewer,
1076
-		$node = null,
1077
-		bool $sharedWithMe = false,
1078
-		bool $reShares = false,
1079
-		bool $subFiles = false,
1080
-		bool $includeTags = false,
1081
-	): array {
1082
-		if ($sharedWithMe) {
1083
-			return $this->getSharedWithMe($node, $includeTags);
1084
-		}
1085
-
1086
-		if ($subFiles) {
1087
-			return $this->getSharesInDir($node);
1088
-		}
1089
-
1090
-		$shares = $this->getSharesFromNode($viewer, $node, $reShares);
1091
-
1092
-		$known = $formatted = $miniFormatted = [];
1093
-		$resharingRight = false;
1094
-		foreach ($shares as $share) {
1095
-			try {
1096
-				$share->getNode();
1097
-			} catch (NotFoundException $e) {
1098
-				/*
80
+    private ?Node $lockedNode = null;
81
+    private array $trustedServerCache = [];
82
+
83
+    /**
84
+     * Share20OCS constructor.
85
+     */
86
+    public function __construct(
87
+        string $appName,
88
+        IRequest $request,
89
+        private IManager $shareManager,
90
+        private IGroupManager $groupManager,
91
+        private IUserManager $userManager,
92
+        private IRootFolder $rootFolder,
93
+        private IURLGenerator $urlGenerator,
94
+        private IL10N $l,
95
+        private IConfig $config,
96
+        private IAppConfig $appConfig,
97
+        private IAppManager $appManager,
98
+        private ContainerInterface $serverContainer,
99
+        private IUserStatusManager $userStatusManager,
100
+        private IPreview $previewManager,
101
+        private IDateTimeZone $dateTimeZone,
102
+        private LoggerInterface $logger,
103
+        private IProviderFactory $factory,
104
+        private IMailer $mailer,
105
+        private ITagManager $tagManager,
106
+        private IEmailValidator $emailValidator,
107
+        private ?TrustedServers $trustedServers,
108
+        private ?string $userId = null,
109
+    ) {
110
+        parent::__construct($appName, $request);
111
+    }
112
+
113
+    /**
114
+     * Convert an IShare to an array for OCS output
115
+     *
116
+     * @param IShare $share
117
+     * @param Node|null $recipientNode
118
+     * @return Files_SharingShare
119
+     * @throws NotFoundException In case the node can't be resolved.
120
+     *
121
+     * @suppress PhanUndeclaredClassMethod
122
+     */
123
+    protected function formatShare(IShare $share, ?Node $recipientNode = null): array {
124
+        $sharedBy = $this->userManager->get($share->getSharedBy());
125
+        $shareOwner = $this->userManager->get($share->getShareOwner());
126
+
127
+        $isOwnShare = false;
128
+        if ($shareOwner !== null) {
129
+            $isOwnShare = $shareOwner->getUID() === $this->userId;
130
+        }
131
+
132
+        $result = [
133
+            'id' => $share->getId(),
134
+            'share_type' => $share->getShareType(),
135
+            'uid_owner' => $share->getSharedBy(),
136
+            'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
137
+            // recipient permissions
138
+            'permissions' => $share->getPermissions(),
139
+            // current user permissions on this share
140
+            'can_edit' => $this->canEditShare($share),
141
+            'can_delete' => $this->canDeleteShare($share),
142
+            'stime' => $share->getShareTime()->getTimestamp(),
143
+            'parent' => null,
144
+            'expiration' => null,
145
+            'token' => null,
146
+            'uid_file_owner' => $share->getShareOwner(),
147
+            'note' => $share->getNote(),
148
+            'label' => $share->getLabel(),
149
+            'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
150
+        ];
151
+
152
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
153
+        if ($recipientNode) {
154
+            $node = $recipientNode;
155
+        } else {
156
+            $node = $userFolder->getFirstNodeById($share->getNodeId());
157
+            if (!$node) {
158
+                // fallback to guessing the path
159
+                $node = $userFolder->get($share->getTarget());
160
+                if ($node === null || $share->getTarget() === '') {
161
+                    throw new NotFoundException();
162
+                }
163
+            }
164
+        }
165
+
166
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
167
+        if ($node instanceof Folder) {
168
+            $result['item_type'] = 'folder';
169
+        } else {
170
+            $result['item_type'] = 'file';
171
+        }
172
+
173
+        // Get the original node permission if the share owner is the current user
174
+        if ($isOwnShare) {
175
+            $result['item_permissions'] = $node->getPermissions();
176
+        }
177
+
178
+        // If we're on the recipient side, the node permissions
179
+        // are bound to the share permissions. So we need to
180
+        // adjust the permissions to the share permissions if necessary.
181
+        if (!$isOwnShare) {
182
+            $result['item_permissions'] = $share->getPermissions();
183
+
184
+            // For some reason, single files share are forbidden to have the delete permission
185
+            // since we have custom methods to check those, let's adjust straight away.
186
+            // DAV permissions does not have that issue though.
187
+            if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
188
+                $result['item_permissions'] |= Constants::PERMISSION_DELETE;
189
+            }
190
+            if ($this->canEditShare($share)) {
191
+                $result['item_permissions'] |= Constants::PERMISSION_UPDATE;
192
+            }
193
+        }
194
+
195
+        // See MOUNT_ROOT_PROPERTYNAME dav property
196
+        $result['is-mount-root'] = $node->getInternalPath() === '';
197
+        $result['mount-type'] = $node->getMountPoint()->getMountType();
198
+
199
+        $result['mimetype'] = $node->getMimetype();
200
+        $result['has_preview'] = $this->previewManager->isAvailable($node);
201
+        $result['storage_id'] = $node->getStorage()->getId();
202
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
203
+        $result['item_source'] = $node->getId();
204
+        $result['file_source'] = $node->getId();
205
+        $result['file_parent'] = $node->getParent()->getId();
206
+        $result['file_target'] = $share->getTarget();
207
+        $result['item_size'] = $node->getSize();
208
+        $result['item_mtime'] = $node->getMTime();
209
+
210
+        if ($this->trustedServers !== null && in_array($share->getShareType(), [IShare::TYPE_REMOTE, IShare::TYPE_REMOTE_GROUP], true)) {
211
+            $result['is_trusted_server'] = false;
212
+            $sharedWith = $share->getSharedWith();
213
+            $remoteIdentifier = is_string($sharedWith) ? strrchr($sharedWith, '@') : false;
214
+            if ($remoteIdentifier !== false) {
215
+                $remote = substr($remoteIdentifier, 1);
216
+
217
+                if (isset($this->trustedServerCache[$remote])) {
218
+                    $result['is_trusted_server'] = $this->trustedServerCache[$remote];
219
+                } else {
220
+                    try {
221
+                        $isTrusted = $this->trustedServers->isTrustedServer($remote);
222
+                        $this->trustedServerCache[$remote] = $isTrusted;
223
+                        $result['is_trusted_server'] = $isTrusted;
224
+                    } catch (\Exception $e) {
225
+                        // Server not found or other issue, we consider it not trusted
226
+                        $this->trustedServerCache[$remote] = false;
227
+                        $this->logger->error(
228
+                            'Error checking if remote server is trusted (treating as untrusted): ' . $e->getMessage(),
229
+                            ['exception' => $e]
230
+                        );
231
+                    }
232
+                }
233
+            }
234
+        }
235
+
236
+        $expiration = $share->getExpirationDate();
237
+        if ($expiration !== null) {
238
+            $expiration->setTimezone($this->dateTimeZone->getTimeZone());
239
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
240
+        }
241
+
242
+        if ($share->getShareType() === IShare::TYPE_USER) {
243
+            $sharedWith = $this->userManager->get($share->getSharedWith());
244
+            $result['share_with'] = $share->getSharedWith();
245
+            $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
246
+            $result['share_with_displayname_unique'] = $sharedWith !== null ? (
247
+                !empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
248
+            ) : $share->getSharedWith();
249
+
250
+            $userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
251
+            $userStatus = array_shift($userStatuses);
252
+            if ($userStatus) {
253
+                $result['status'] = [
254
+                    'status' => $userStatus->getStatus(),
255
+                    'message' => $userStatus->getMessage(),
256
+                    'icon' => $userStatus->getIcon(),
257
+                    'clearAt' => $userStatus->getClearAt()
258
+                        ? (int)$userStatus->getClearAt()->format('U')
259
+                        : null,
260
+                ];
261
+            }
262
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
263
+            $group = $this->groupManager->get($share->getSharedWith());
264
+            $result['share_with'] = $share->getSharedWith();
265
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
266
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
267
+
268
+            // "share_with" and "share_with_displayname" for passwords of link
269
+            // shares was deprecated in Nextcloud 15, use "password" instead.
270
+            $result['share_with'] = $share->getPassword();
271
+            $result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
272
+
273
+            $result['password'] = $share->getPassword();
274
+
275
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
276
+
277
+            $result['token'] = $share->getToken();
278
+            $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
279
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
280
+            $result['share_with'] = $share->getSharedWith();
281
+            $result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
282
+            $result['token'] = $share->getToken();
283
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
284
+            $result['share_with'] = $share->getSharedWith();
285
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
286
+            $result['token'] = $share->getToken();
287
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
288
+            $result['share_with'] = $share->getSharedWith();
289
+            $result['password'] = $share->getPassword();
290
+            $result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
291
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
292
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
293
+            $result['token'] = $share->getToken();
294
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
295
+            // getSharedWith() returns either "name (type, owner)" or
296
+            // "name (type, owner) [id]", depending on the Teams app version.
297
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
298
+
299
+            $result['share_with_displayname'] = $share->getSharedWithDisplayName();
300
+            if (empty($result['share_with_displayname'])) {
301
+                $displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
302
+                $result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
303
+            }
304
+
305
+            $result['share_with_avatar'] = $share->getSharedWithAvatar();
306
+
307
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
308
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
309
+            if ($shareWithLength === false) {
310
+                $result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
311
+            } else {
312
+                $result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
313
+            }
314
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
315
+            $result['share_with'] = $share->getSharedWith();
316
+            $result['share_with_displayname'] = '';
317
+
318
+            try {
319
+                /** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
320
+                $roomShare = $this->getRoomShareHelper()->formatShare($share);
321
+                $result = array_merge($result, $roomShare);
322
+            } catch (ContainerExceptionInterface $e) {
323
+            }
324
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
325
+            $result['share_with'] = $share->getSharedWith();
326
+            $result['share_with_displayname'] = '';
327
+
328
+            try {
329
+                /** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
330
+                $deckShare = $this->getDeckShareHelper()->formatShare($share);
331
+                $result = array_merge($result, $deckShare);
332
+            } catch (ContainerExceptionInterface $e) {
333
+            }
334
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
335
+            $result['share_with'] = $share->getSharedWith();
336
+            $result['share_with_displayname'] = '';
337
+
338
+            try {
339
+                /** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
340
+                $scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
341
+                $result = array_merge($result, $scienceMeshShare);
342
+            } catch (ContainerExceptionInterface $e) {
343
+            }
344
+        }
345
+
346
+
347
+        $result['mail_send'] = $share->getMailSend() ? 1 : 0;
348
+        $result['hide_download'] = $share->getHideDownload() ? 1 : 0;
349
+
350
+        $result['attributes'] = null;
351
+        if ($attributes = $share->getAttributes()) {
352
+            $result['attributes'] = (string)\json_encode($attributes->toArray());
353
+        }
354
+
355
+        return $result;
356
+    }
357
+
358
+    /**
359
+     * Check if one of the users address books knows the exact property, if
360
+     * not we return the full name.
361
+     *
362
+     * @param string $query
363
+     * @param string $property
364
+     * @return string
365
+     */
366
+    private function getDisplayNameFromAddressBook(string $query, string $property): string {
367
+        // FIXME: If we inject the contacts manager it gets initialized before any address books are registered
368
+        try {
369
+            $result = Server::get(\OCP\Contacts\IManager::class)->search($query, [$property], [
370
+                'limit' => 1,
371
+                'enumeration' => false,
372
+                'strict_search' => true,
373
+            ]);
374
+        } catch (Exception $e) {
375
+            $this->logger->error(
376
+                $e->getMessage(),
377
+                ['exception' => $e]
378
+            );
379
+            return $query;
380
+        }
381
+
382
+        foreach ($result as $r) {
383
+            foreach ($r[$property] as $value) {
384
+                if ($value === $query && $r['FN']) {
385
+                    return $r['FN'];
386
+                }
387
+            }
388
+        }
389
+
390
+        return $query;
391
+    }
392
+
393
+
394
+    /**
395
+     * @param list<Files_SharingShare> $shares
396
+     * @param array<string, string>|null $updatedDisplayName
397
+     *
398
+     * @return list<Files_SharingShare>
399
+     */
400
+    private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
401
+        $userIds = $updated = [];
402
+        foreach ($shares as $share) {
403
+            // share is federated and share have no display name yet
404
+            if ($share['share_type'] === IShare::TYPE_REMOTE
405
+                && ($share['share_with'] ?? '') !== ''
406
+                && ($share['share_with_displayname'] ?? '') === '') {
407
+                $userIds[] = $userId = $share['share_with'];
408
+
409
+                if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
410
+                    $share['share_with_displayname'] = $updatedDisplayName[$userId];
411
+                }
412
+            }
413
+
414
+            // prepping userIds with displayName to be updated
415
+            $updated[] = $share;
416
+        }
417
+
418
+        // if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
419
+        if ($updatedDisplayName !== null) {
420
+            return $updated;
421
+        }
422
+
423
+        // get displayName for the generated list of userId with no displayName
424
+        $displayNames = $this->retrieveFederatedDisplayName($userIds);
425
+
426
+        // if no displayName are updated, we exit
427
+        if (empty($displayNames)) {
428
+            return $updated;
429
+        }
430
+
431
+        // let's fix missing display name and returns all shares
432
+        return $this->fixMissingDisplayName($shares, $displayNames);
433
+    }
434
+
435
+
436
+    /**
437
+     * get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
438
+     * returns an array with userIds as keys and displayName as values.
439
+     *
440
+     * @param array $userIds
441
+     * @param bool $cacheOnly - do not reach LUS, get data from cache.
442
+     *
443
+     * @return array
444
+     * @throws ContainerExceptionInterface
445
+     */
446
+    private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
447
+        // check if gss is enabled and available
448
+        if (count($userIds) === 0
449
+            || !$this->appManager->isEnabledForAnyone('globalsiteselector')
450
+            || !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
451
+            return [];
452
+        }
453
+
454
+        try {
455
+            $slaveService = Server::get(SlaveService::class);
456
+        } catch (\Throwable $e) {
457
+            $this->logger->error(
458
+                $e->getMessage(),
459
+                ['exception' => $e]
460
+            );
461
+            return [];
462
+        }
463
+
464
+        return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
465
+    }
466
+
467
+
468
+    /**
469
+     * retrieve displayName from cache if available (should be used on federated shares)
470
+     * if not available in cache/lus, try for get from address-book, else returns empty string.
471
+     *
472
+     * @param string $userId
473
+     * @param bool $cacheOnly if true will not reach the lus but will only get data from cache
474
+     *
475
+     * @return string
476
+     */
477
+    private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
478
+        $details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
479
+        if (array_key_exists($userId, $details)) {
480
+            return $details[$userId];
481
+        }
482
+
483
+        $displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
484
+        return ($displayName === $userId) ? '' : $displayName;
485
+    }
486
+
487
+
488
+
489
+    /**
490
+     * Get a specific share by id
491
+     *
492
+     * @param string $id ID of the share
493
+     * @param bool $include_tags Include tags in the share
494
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
495
+     * @throws OCSNotFoundException Share not found
496
+     *
497
+     * 200: Share returned
498
+     */
499
+    #[NoAdminRequired]
500
+    public function getShare(string $id, bool $include_tags = false): DataResponse {
501
+        try {
502
+            $share = $this->getShareById($id);
503
+        } catch (ShareNotFound $e) {
504
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
505
+        }
506
+
507
+        try {
508
+            if ($this->canAccessShare($share)) {
509
+                $share = $this->formatShare($share);
510
+
511
+                if ($include_tags) {
512
+                    $share = $this->populateTags([$share]);
513
+                } else {
514
+                    $share = [$share];
515
+                }
516
+
517
+                return new DataResponse($share);
518
+            }
519
+        } catch (NotFoundException $e) {
520
+            // Fall through
521
+        }
522
+
523
+        throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
524
+    }
525
+
526
+    /**
527
+     * Delete a share
528
+     *
529
+     * @param string $id ID of the share
530
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
531
+     * @throws OCSNotFoundException Share not found
532
+     * @throws OCSForbiddenException Missing permissions to delete the share
533
+     *
534
+     * 200: Share deleted successfully
535
+     */
536
+    #[NoAdminRequired]
537
+    public function deleteShare(string $id): DataResponse {
538
+        try {
539
+            $share = $this->getShareById($id);
540
+        } catch (ShareNotFound $e) {
541
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
542
+        }
543
+
544
+        try {
545
+            $this->lock($share->getNode());
546
+        } catch (LockedException $e) {
547
+            throw new OCSNotFoundException($this->l->t('Could not delete share'));
548
+        }
549
+
550
+        if (!$this->canAccessShare($share)) {
551
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
552
+        }
553
+
554
+        // if it's a group share or a room share
555
+        // we don't delete the share, but only the
556
+        // mount point. Allowing it to be restored
557
+        // from the deleted shares
558
+        if ($this->canDeleteShareFromSelf($share)) {
559
+            $this->shareManager->deleteFromSelf($share, $this->userId);
560
+        } else {
561
+            if (!$this->canDeleteShare($share)) {
562
+                throw new OCSForbiddenException($this->l->t('Could not delete share'));
563
+            }
564
+
565
+            $this->shareManager->deleteShare($share);
566
+        }
567
+
568
+        return new DataResponse();
569
+    }
570
+
571
+    /**
572
+     * Create a share
573
+     *
574
+     * @param string|null $path Path of the share
575
+     * @param int|null $permissions Permissions for the share
576
+     * @param int $shareType Type of the share
577
+     * @param ?string $shareWith The entity this should be shared with
578
+     * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
579
+     * @param string $password Password for the share
580
+     * @param string|null $sendPasswordByTalk Send the password for the share over Talk
581
+     * @param ?string $expireDate The expiry date of the share in the user's timezone at 00:00.
582
+     *                            If $expireDate is not supplied or set to `null`, the system default will be used.
583
+     * @param string $note Note for the share
584
+     * @param string $label Label for the share (only used in link and email)
585
+     * @param string|null $attributes Additional attributes for the share
586
+     * @param 'false'|'true'|null $sendMail Send a mail to the recipient
587
+     *
588
+     * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
589
+     * @throws OCSBadRequestException Unknown share type
590
+     * @throws OCSException
591
+     * @throws OCSForbiddenException Creating the share is not allowed
592
+     * @throws OCSNotFoundException Creating the share failed
593
+     * @suppress PhanUndeclaredClassMethod
594
+     *
595
+     * 200: Share created
596
+     */
597
+    #[NoAdminRequired]
598
+    #[UserRateLimit(limit: 20, period: 600)]
599
+    public function createShare(
600
+        ?string $path = null,
601
+        ?int $permissions = null,
602
+        int $shareType = -1,
603
+        ?string $shareWith = null,
604
+        ?string $publicUpload = null,
605
+        string $password = '',
606
+        ?string $sendPasswordByTalk = null,
607
+        ?string $expireDate = null,
608
+        string $note = '',
609
+        string $label = '',
610
+        ?string $attributes = null,
611
+        ?string $sendMail = null,
612
+    ): DataResponse {
613
+        assert($this->userId !== null);
614
+
615
+        $share = $this->shareManager->newShare();
616
+        $hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
617
+
618
+        // Verify path
619
+        if ($path === null) {
620
+            throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
621
+        }
622
+
623
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
624
+        try {
625
+            /** @var \OC\Files\Node\Node $node */
626
+            $node = $userFolder->get($path);
627
+        } catch (NotFoundException $e) {
628
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
629
+        }
630
+
631
+        // a user can have access to a file through different paths, with differing permissions
632
+        // combine all permissions to determine if the user can share this file
633
+        $nodes = $userFolder->getById($node->getId());
634
+        foreach ($nodes as $nodeById) {
635
+            /** @var FileInfo $fileInfo */
636
+            $fileInfo = $node->getFileInfo();
637
+            $fileInfo['permissions'] |= $nodeById->getPermissions();
638
+        }
639
+
640
+        $share->setNode($node);
641
+
642
+        try {
643
+            $this->lock($share->getNode());
644
+        } catch (LockedException $e) {
645
+            throw new OCSNotFoundException($this->l->t('Could not create share'));
646
+        }
647
+
648
+        // Set permissions
649
+        if ($shareType === IShare::TYPE_LINK || $shareType === IShare::TYPE_EMAIL) {
650
+            $permissions = $this->getLinkSharePermissions($permissions, $hasPublicUpload);
651
+            $this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
652
+        } else {
653
+            // Use default permissions only for non-link shares to keep legacy behavior
654
+            if ($permissions === null) {
655
+                $permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
656
+            }
657
+            // Non-link shares always require read permissions (link shares could be file drop)
658
+            $permissions |= Constants::PERMISSION_READ;
659
+        }
660
+
661
+        // For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
662
+        if ($node instanceof File) {
663
+            // if this is a single file share we remove the DELETE and CREATE permissions
664
+            $permissions = $permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE);
665
+        }
666
+
667
+        /**
668
+         * Hack for https://github.com/owncloud/core/issues/22587
669
+         * We check the permissions via webdav. But the permissions of the mount point
670
+         * do not equal the share permissions. Here we fix that for federated mounts.
671
+         */
672
+        if ($node->getStorage()->instanceOfStorage(Storage::class)) {
673
+            $permissions &= ~($permissions & ~$node->getPermissions());
674
+        }
675
+
676
+        if ($attributes !== null) {
677
+            $share = $this->setShareAttributes($share, $attributes);
678
+        }
679
+
680
+        // Expire date checks
681
+        // Normally, null means no expiration date but we still set the default for backwards compatibility
682
+        // If the client sends an empty string, we set noExpirationDate to true
683
+        if ($expireDate !== null) {
684
+            if ($expireDate !== '') {
685
+                try {
686
+                    $expireDateTime = $this->parseDate($expireDate);
687
+                    $share->setExpirationDate($expireDateTime);
688
+                } catch (\Exception $e) {
689
+                    throw new OCSNotFoundException($e->getMessage(), $e);
690
+                }
691
+            } else {
692
+                // Client sent empty string for expire date.
693
+                // Set noExpirationDate to true so overwrite is prevented.
694
+                $share->setNoExpirationDate(true);
695
+            }
696
+        }
697
+
698
+        $share->setSharedBy($this->userId);
699
+
700
+        // Handle mail send
701
+        if (is_null($sendMail)) {
702
+            $allowSendMail = $this->config->getSystemValueBool('sharing.enable_share_mail', true);
703
+            if ($allowSendMail !== true || $shareType === IShare::TYPE_EMAIL) {
704
+                // Define a default behavior when sendMail is not provided
705
+                // For email shares with a valid recipient, the default is to send the mail
706
+                // For all other share types, the default is to not send the mail
707
+                $allowSendMail = ($shareType === IShare::TYPE_EMAIL && $shareWith !== null && $shareWith !== '');
708
+            }
709
+            $share->setMailSend($allowSendMail);
710
+        } else {
711
+            $share->setMailSend($sendMail === 'true');
712
+        }
713
+
714
+        if ($shareType === IShare::TYPE_USER) {
715
+            // Valid user is required to share
716
+            if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
717
+                throw new OCSNotFoundException($this->l->t('Please specify a valid account to share with'));
718
+            }
719
+            $share->setSharedWith($shareWith);
720
+            $share->setPermissions($permissions);
721
+        } elseif ($shareType === IShare::TYPE_GROUP) {
722
+            if (!$this->shareManager->allowGroupSharing()) {
723
+                throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
724
+            }
725
+
726
+            // Valid group is required to share
727
+            if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
728
+                throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
729
+            }
730
+            $share->setSharedWith($shareWith);
731
+            $share->setPermissions($permissions);
732
+        } elseif ($shareType === IShare::TYPE_LINK
733
+            || $shareType === IShare::TYPE_EMAIL) {
734
+
735
+            // Can we even share links?
736
+            if (!$this->shareManager->shareApiAllowLinks()) {
737
+                throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
738
+            }
739
+
740
+            $this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
741
+            $share->setPermissions($permissions);
742
+
743
+            // Set password
744
+            if ($password !== '') {
745
+                $share->setPassword($password);
746
+            }
747
+
748
+            // Only share by mail have a recipient
749
+            if (is_string($shareWith) && $shareType === IShare::TYPE_EMAIL) {
750
+                // If sending a mail have been requested, validate the mail address
751
+                if ($share->getMailSend() && !$this->emailValidator->isValid($shareWith)) {
752
+                    throw new OCSNotFoundException($this->l->t('Please specify a valid email address'));
753
+                }
754
+                $share->setSharedWith($shareWith);
755
+            }
756
+
757
+            // If we have a label, use it
758
+            if ($label !== '') {
759
+                if (strlen($label) > 255) {
760
+                    throw new OCSBadRequestException('Maximum label length is 255');
761
+                }
762
+                $share->setLabel($label);
763
+            }
764
+
765
+            if ($sendPasswordByTalk === 'true') {
766
+                if (!$this->appManager->isEnabledForUser('spreed')) {
767
+                    throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$node->getPath()]));
768
+                }
769
+
770
+                $share->setSendPasswordByTalk(true);
771
+            }
772
+        } elseif ($shareType === IShare::TYPE_REMOTE) {
773
+            if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
774
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
775
+            }
776
+
777
+            if ($shareWith === null) {
778
+                throw new OCSNotFoundException($this->l->t('Please specify a valid federated account ID'));
779
+            }
780
+
781
+            $share->setSharedWith($shareWith);
782
+            $share->setPermissions($permissions);
783
+            $share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
784
+        } elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
785
+            if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
786
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
787
+            }
788
+
789
+            if ($shareWith === null) {
790
+                throw new OCSNotFoundException($this->l->t('Please specify a valid federated group ID'));
791
+            }
792
+
793
+            $share->setSharedWith($shareWith);
794
+            $share->setPermissions($permissions);
795
+        } elseif ($shareType === IShare::TYPE_CIRCLE) {
796
+            if (!Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
797
+                throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
798
+            }
799
+
800
+            $circle = Circles::detailsCircle($shareWith);
801
+
802
+            // Valid team is required to share
803
+            if ($circle === null) {
804
+                throw new OCSNotFoundException($this->l->t('Please specify a valid team'));
805
+            }
806
+            $share->setSharedWith($shareWith);
807
+            $share->setPermissions($permissions);
808
+        } elseif ($shareType === IShare::TYPE_ROOM) {
809
+            try {
810
+                $this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
811
+            } catch (ContainerExceptionInterface $e) {
812
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
813
+            }
814
+        } elseif ($shareType === IShare::TYPE_DECK) {
815
+            try {
816
+                $this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
817
+            } catch (ContainerExceptionInterface $e) {
818
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
819
+            }
820
+        } elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
821
+            try {
822
+                $this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
823
+            } catch (ContainerExceptionInterface $e) {
824
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
825
+            }
826
+        } else {
827
+            throw new OCSBadRequestException($this->l->t('Unknown share type'));
828
+        }
829
+
830
+        $share->setShareType($shareType);
831
+        $this->checkInheritedAttributes($share);
832
+
833
+        if ($note !== '') {
834
+            $share->setNote($note);
835
+        }
836
+
837
+        try {
838
+            $share = $this->shareManager->createShare($share);
839
+        } catch (HintException $e) {
840
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
841
+            throw new OCSException($e->getHint(), $code);
842
+        } catch (GenericShareException|\InvalidArgumentException $e) {
843
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
844
+            throw new OCSForbiddenException($e->getMessage(), $e);
845
+        } catch (\Exception $e) {
846
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
847
+            throw new OCSForbiddenException('Failed to create share.', $e);
848
+        }
849
+
850
+        $output = $this->formatShare($share);
851
+
852
+        return new DataResponse($output);
853
+    }
854
+
855
+    /**
856
+     * @param null|Node $node
857
+     * @param boolean $includeTags
858
+     *
859
+     * @return list<Files_SharingShare>
860
+     */
861
+    private function getSharedWithMe($node, bool $includeTags): array {
862
+        $userShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_USER, $node, -1, 0);
863
+        $groupShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_GROUP, $node, -1, 0);
864
+        $circleShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_CIRCLE, $node, -1, 0);
865
+        $roomShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_ROOM, $node, -1, 0);
866
+        $deckShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_DECK, $node, -1, 0);
867
+        $sciencemeshShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, $node, -1, 0);
868
+
869
+        $shares = array_merge($userShares, $groupShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares);
870
+
871
+        $filteredShares = array_filter($shares, function (IShare $share) {
872
+            return $share->getShareOwner() !== $this->userId && $share->getSharedBy() !== $this->userId;
873
+        });
874
+
875
+        $formatted = [];
876
+        foreach ($filteredShares as $share) {
877
+            if ($this->canAccessShare($share)) {
878
+                try {
879
+                    $formatted[] = $this->formatShare($share);
880
+                } catch (NotFoundException $e) {
881
+                    // Ignore this share
882
+                }
883
+            }
884
+        }
885
+
886
+        if ($includeTags) {
887
+            $formatted = $this->populateTags($formatted);
888
+        }
889
+
890
+        return $formatted;
891
+    }
892
+
893
+    /**
894
+     * @param Node $folder
895
+     *
896
+     * @return list<Files_SharingShare>
897
+     * @throws OCSBadRequestException
898
+     * @throws NotFoundException
899
+     */
900
+    private function getSharesInDir(Node $folder): array {
901
+        if (!($folder instanceof Folder)) {
902
+            throw new OCSBadRequestException($this->l->t('Not a directory'));
903
+        }
904
+
905
+        $nodes = $folder->getDirectoryListing();
906
+
907
+        /** @var IShare[] $shares */
908
+        $shares = array_reduce($nodes, function ($carry, $node) {
909
+            $carry = array_merge($carry, $this->getAllShares($node, true));
910
+            return $carry;
911
+        }, []);
912
+
913
+        // filter out duplicate shares
914
+        $known = [];
915
+
916
+        $formatted = $miniFormatted = [];
917
+        $resharingRight = false;
918
+        $known = [];
919
+        foreach ($shares as $share) {
920
+            if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->userId) {
921
+                continue;
922
+            }
923
+
924
+            try {
925
+                $format = $this->formatShare($share);
926
+
927
+                $known[] = $share->getId();
928
+                $formatted[] = $format;
929
+                if ($share->getSharedBy() === $this->userId) {
930
+                    $miniFormatted[] = $format;
931
+                }
932
+                if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $folder)) {
933
+                    $resharingRight = true;
934
+                }
935
+            } catch (\Exception $e) {
936
+                //Ignore this share
937
+            }
938
+        }
939
+
940
+        if (!$resharingRight) {
941
+            $formatted = $miniFormatted;
942
+        }
943
+
944
+        return $formatted;
945
+    }
946
+
947
+    /**
948
+     * Get shares of the current user
949
+     *
950
+     * @param string $shared_with_me Only get shares with the current user
951
+     * @param string $reshares Only get shares by the current user and reshares
952
+     * @param string $subfiles Only get all shares in a folder
953
+     * @param string $path Get shares for a specific path
954
+     * @param string $include_tags Include tags in the share
955
+     *
956
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
957
+     * @throws OCSNotFoundException The folder was not found or is inaccessible
958
+     *
959
+     * 200: Shares returned
960
+     */
961
+    #[NoAdminRequired]
962
+    public function getShares(
963
+        string $shared_with_me = 'false',
964
+        string $reshares = 'false',
965
+        string $subfiles = 'false',
966
+        string $path = '',
967
+        string $include_tags = 'false',
968
+    ): DataResponse {
969
+        $node = null;
970
+        if ($path !== '') {
971
+            $userFolder = $this->rootFolder->getUserFolder($this->userId);
972
+            try {
973
+                $node = $userFolder->get($path);
974
+                $this->lock($node);
975
+            } catch (NotFoundException $e) {
976
+                throw new OCSNotFoundException(
977
+                    $this->l->t('Wrong path, file/folder does not exist')
978
+                );
979
+            } catch (LockedException $e) {
980
+                throw new OCSNotFoundException($this->l->t('Could not lock node'));
981
+            }
982
+        }
983
+
984
+        $shares = $this->getFormattedShares(
985
+            $this->userId,
986
+            $node,
987
+            ($shared_with_me === 'true'),
988
+            ($reshares === 'true'),
989
+            ($subfiles === 'true'),
990
+            ($include_tags === 'true')
991
+        );
992
+
993
+        return new DataResponse($shares);
994
+    }
995
+
996
+    private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicUpload): int {
997
+        $permissions = $permissions ?? Constants::PERMISSION_READ;
998
+
999
+        // Legacy option handling
1000
+        if ($legacyPublicUpload !== null) {
1001
+            $permissions = $legacyPublicUpload
1002
+                ? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1003
+                : Constants::PERMISSION_READ;
1004
+        }
1005
+
1006
+        if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
1007
+            && $this->shareManager->outgoingServer2ServerSharesAllowed()
1008
+            && $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES)) {
1009
+            $permissions |= Constants::PERMISSION_SHARE;
1010
+        }
1011
+
1012
+        return $permissions;
1013
+    }
1014
+
1015
+    /**
1016
+     * Helper to check for legacy "publicUpload" handling.
1017
+     * If the value is set to `true` or `false` then true or false are returned.
1018
+     * Otherwise null is returned to indicate that the option was not (or wrong) set.
1019
+     *
1020
+     * @param null|string $legacyPublicUpload The value of `publicUpload`
1021
+     */
1022
+    private function getLegacyPublicUpload(?string $legacyPublicUpload): ?bool {
1023
+        if ($legacyPublicUpload === 'true') {
1024
+            return true;
1025
+        } elseif ($legacyPublicUpload === 'false') {
1026
+            return false;
1027
+        }
1028
+        // Not set at all
1029
+        return null;
1030
+    }
1031
+
1032
+    /**
1033
+     * For link and email shares validate that only allowed combinations are set.
1034
+     *
1035
+     * @throw OCSBadRequestException If permission combination is invalid.
1036
+     * @throw OCSForbiddenException If public upload was forbidden by the administrator.
1037
+     */
1038
+    private function validateLinkSharePermissions(Node $node, int $permissions, ?bool $legacyPublicUpload): void {
1039
+        if ($legacyPublicUpload && ($node instanceof File)) {
1040
+            throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1041
+        }
1042
+
1043
+        // We need at least READ or CREATE (file drop)
1044
+        if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1045
+            && !$this->hasPermission($permissions, Constants::PERMISSION_CREATE)) {
1046
+            throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1047
+        }
1048
+
1049
+        // UPDATE and DELETE require a READ permission
1050
+        if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1051
+            && ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
1052
+            throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1053
+        }
1054
+
1055
+        // Check if public uploading was disabled
1056
+        if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1057
+            && !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1058
+            throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1059
+        }
1060
+    }
1061
+
1062
+    /**
1063
+     * @param string $viewer
1064
+     * @param Node $node
1065
+     * @param bool $sharedWithMe
1066
+     * @param bool $reShares
1067
+     * @param bool $subFiles
1068
+     * @param bool $includeTags
1069
+     *
1070
+     * @return list<Files_SharingShare>
1071
+     * @throws NotFoundException
1072
+     * @throws OCSBadRequestException
1073
+     */
1074
+    private function getFormattedShares(
1075
+        string $viewer,
1076
+        $node = null,
1077
+        bool $sharedWithMe = false,
1078
+        bool $reShares = false,
1079
+        bool $subFiles = false,
1080
+        bool $includeTags = false,
1081
+    ): array {
1082
+        if ($sharedWithMe) {
1083
+            return $this->getSharedWithMe($node, $includeTags);
1084
+        }
1085
+
1086
+        if ($subFiles) {
1087
+            return $this->getSharesInDir($node);
1088
+        }
1089
+
1090
+        $shares = $this->getSharesFromNode($viewer, $node, $reShares);
1091
+
1092
+        $known = $formatted = $miniFormatted = [];
1093
+        $resharingRight = false;
1094
+        foreach ($shares as $share) {
1095
+            try {
1096
+                $share->getNode();
1097
+            } catch (NotFoundException $e) {
1098
+                /*
1099 1099
 				 * Ignore shares where we can't get the node
1100 1100
 				 * For example deleted shares
1101 1101
 				 */
1102
-				continue;
1103
-			}
1104
-
1105
-			if (in_array($share->getId(), $known)
1106
-				|| ($share->getSharedWith() === $this->userId && $share->getShareType() === IShare::TYPE_USER)) {
1107
-				continue;
1108
-			}
1109
-
1110
-			$known[] = $share->getId();
1111
-			try {
1112
-				/** @var IShare $share */
1113
-				$format = $this->formatShare($share, $node);
1114
-				$formatted[] = $format;
1115
-
1116
-				// let's also build a list of shares created
1117
-				// by the current user only, in case
1118
-				// there is no resharing rights
1119
-				if ($share->getSharedBy() === $this->userId) {
1120
-					$miniFormatted[] = $format;
1121
-				}
1122
-
1123
-				// check if one of those share is shared with me
1124
-				// and if I have resharing rights on it
1125
-				if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $node)) {
1126
-					$resharingRight = true;
1127
-				}
1128
-			} catch (InvalidPathException|NotFoundException $e) {
1129
-			}
1130
-		}
1131
-
1132
-		if (!$resharingRight) {
1133
-			$formatted = $miniFormatted;
1134
-		}
1135
-
1136
-		// fix eventual missing display name from federated shares
1137
-		$formatted = $this->fixMissingDisplayName($formatted);
1138
-
1139
-		if ($includeTags) {
1140
-			$formatted = $this->populateTags($formatted);
1141
-		}
1142
-
1143
-		return $formatted;
1144
-	}
1145
-
1146
-
1147
-	/**
1148
-	 * Get all shares relative to a file, including parent folders shares rights
1149
-	 *
1150
-	 * @param string $path Path all shares will be relative to
1151
-	 *
1152
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1153
-	 * @throws InvalidPathException
1154
-	 * @throws NotFoundException
1155
-	 * @throws OCSNotFoundException The given path is invalid
1156
-	 * @throws SharingRightsException
1157
-	 *
1158
-	 * 200: Shares returned
1159
-	 */
1160
-	#[NoAdminRequired]
1161
-	public function getInheritedShares(string $path): DataResponse {
1162
-		// get Node from (string) path.
1163
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1164
-		try {
1165
-			$node = $userFolder->get($path);
1166
-			$this->lock($node);
1167
-		} catch (NotFoundException $e) {
1168
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
1169
-		} catch (LockedException $e) {
1170
-			throw new OCSNotFoundException($this->l->t('Could not lock path'));
1171
-		}
1172
-
1173
-		if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
1174
-			throw new SharingRightsException($this->l->t('no sharing rights on this item'));
1175
-		}
1176
-
1177
-		// The current top parent we have access to
1178
-		$parent = $node;
1179
-
1180
-		// initiate real owner.
1181
-		$owner = $node->getOwner()
1182
-			->getUID();
1183
-		if (!$this->userManager->userExists($owner)) {
1184
-			return new DataResponse([]);
1185
-		}
1186
-
1187
-		// get node based on the owner, fix owner in case of external storage
1188
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1189
-		if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
1190
-			$owner = $node->getOwner()
1191
-				->getUID();
1192
-			$userFolder = $this->rootFolder->getUserFolder($owner);
1193
-			$node = $userFolder->getFirstNodeById($node->getId());
1194
-		}
1195
-		$basePath = $userFolder->getPath();
1196
-
1197
-		// generate node list for each parent folders
1198
-		/** @var Node[] $nodes */
1199
-		$nodes = [];
1200
-		while (true) {
1201
-			$node = $node->getParent();
1202
-			if ($node->getPath() === $basePath) {
1203
-				break;
1204
-			}
1205
-			$nodes[] = $node;
1206
-		}
1207
-
1208
-		// The user that is requesting this list
1209
-		$currentUserFolder = $this->rootFolder->getUserFolder($this->userId);
1210
-
1211
-		// for each nodes, retrieve shares.
1212
-		$shares = [];
1213
-
1214
-		foreach ($nodes as $node) {
1215
-			$getShares = $this->getFormattedShares($owner, $node, false, true);
1216
-
1217
-			$currentUserNode = $currentUserFolder->getFirstNodeById($node->getId());
1218
-			if ($currentUserNode) {
1219
-				$parent = $currentUserNode;
1220
-			}
1221
-
1222
-			$subPath = $currentUserFolder->getRelativePath($parent->getPath());
1223
-			foreach ($getShares as &$share) {
1224
-				$share['via_fileid'] = $parent->getId();
1225
-				$share['via_path'] = $subPath;
1226
-			}
1227
-			$this->mergeFormattedShares($shares, $getShares);
1228
-		}
1229
-
1230
-		return new DataResponse(array_values($shares));
1231
-	}
1232
-
1233
-	/**
1234
-	 * Check whether a set of permissions contains the permissions to check.
1235
-	 */
1236
-	private function hasPermission(int $permissionsSet, int $permissionsToCheck): bool {
1237
-		return ($permissionsSet & $permissionsToCheck) === $permissionsToCheck;
1238
-	}
1239
-
1240
-	/**
1241
-	 * Update a share
1242
-	 *
1243
-	 * @param string $id ID of the share
1244
-	 * @param int|null $permissions New permissions
1245
-	 * @param string|null $password New password
1246
-	 * @param string|null $sendPasswordByTalk New condition if the password should be send over Talk
1247
-	 * @param string|null $publicUpload New condition if public uploading is allowed
1248
-	 * @param string|null $expireDate New expiry date
1249
-	 * @param string|null $note New note
1250
-	 * @param string|null $label New label
1251
-	 * @param string|null $hideDownload New condition if the download should be hidden
1252
-	 * @param string|null $attributes New additional attributes
1253
-	 * @param string|null $sendMail if the share should be send by mail.
1254
-	 *                              Considering the share already exists, no mail will be send after the share is updated.
1255
-	 *                              You will have to use the sendMail action to send the mail.
1256
-	 * @param string|null $shareWith New recipient for email shares
1257
-	 * @param string|null $token New token
1258
-	 * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
1259
-	 * @throws OCSBadRequestException Share could not be updated because the requested changes are invalid
1260
-	 * @throws OCSForbiddenException Missing permissions to update the share
1261
-	 * @throws OCSNotFoundException Share not found
1262
-	 *
1263
-	 * 200: Share updated successfully
1264
-	 */
1265
-	#[NoAdminRequired]
1266
-	public function updateShare(
1267
-		string $id,
1268
-		?int $permissions = null,
1269
-		?string $password = null,
1270
-		?string $sendPasswordByTalk = null,
1271
-		?string $publicUpload = null,
1272
-		?string $expireDate = null,
1273
-		?string $note = null,
1274
-		?string $label = null,
1275
-		?string $hideDownload = null,
1276
-		?string $attributes = null,
1277
-		?string $sendMail = null,
1278
-		?string $token = null,
1279
-	): DataResponse {
1280
-		try {
1281
-			$share = $this->getShareById($id);
1282
-		} catch (ShareNotFound $e) {
1283
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1284
-		}
1285
-
1286
-		$this->lock($share->getNode());
1287
-
1288
-		if (!$this->canAccessShare($share, false)) {
1289
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1290
-		}
1291
-
1292
-		if (!$this->canEditShare($share)) {
1293
-			throw new OCSForbiddenException($this->l->t('You are not allowed to edit incoming shares'));
1294
-		}
1295
-
1296
-		if (
1297
-			$permissions === null
1298
-			&& $password === null
1299
-			&& $sendPasswordByTalk === null
1300
-			&& $publicUpload === null
1301
-			&& $expireDate === null
1302
-			&& $note === null
1303
-			&& $label === null
1304
-			&& $hideDownload === null
1305
-			&& $attributes === null
1306
-			&& $sendMail === null
1307
-			&& $token === null
1308
-		) {
1309
-			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
1310
-		}
1311
-
1312
-		if ($note !== null) {
1313
-			$share->setNote($note);
1314
-		}
1315
-
1316
-		if ($attributes !== null) {
1317
-			$share = $this->setShareAttributes($share, $attributes);
1318
-		}
1319
-
1320
-		// Handle mail send
1321
-		if ($sendMail === 'true' || $sendMail === 'false') {
1322
-			$share->setMailSend($sendMail === 'true');
1323
-		}
1324
-
1325
-		/**
1326
-		 * expiration date, password and publicUpload only make sense for link shares
1327
-		 */
1328
-		if ($share->getShareType() === IShare::TYPE_LINK
1329
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1330
-
1331
-			// Update hide download state
1332
-			if ($hideDownload === 'true') {
1333
-				$share->setHideDownload(true);
1334
-			} elseif ($hideDownload === 'false') {
1335
-				$share->setHideDownload(false);
1336
-			}
1337
-
1338
-			// If either manual permissions are specified or publicUpload
1339
-			// then we need to also update the permissions of the share
1340
-			if ($permissions !== null || $publicUpload !== null) {
1341
-				$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
1342
-				$permissions = $this->getLinkSharePermissions($permissions ?? Constants::PERMISSION_READ, $hasPublicUpload);
1343
-				$this->validateLinkSharePermissions($share->getNode(), $permissions, $hasPublicUpload);
1344
-				$share->setPermissions($permissions);
1345
-			}
1346
-
1347
-			$passwordParamSent = $password !== null;
1348
-			if ($passwordParamSent) {
1349
-				if ($password === '') {
1350
-					$share->setPassword(null);
1351
-				} else {
1352
-					$share->setPassword($password);
1353
-				}
1354
-			}
1355
-
1356
-			if ($label !== null) {
1357
-				if (strlen($label) > 255) {
1358
-					throw new OCSBadRequestException('Maximum label length is 255');
1359
-				}
1360
-				$share->setLabel($label);
1361
-			}
1362
-
1363
-			if ($sendPasswordByTalk === 'true') {
1364
-				if (!$this->appManager->isEnabledForUser('spreed')) {
1365
-					throw new OCSForbiddenException($this->l->t('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.'));
1366
-				}
1367
-
1368
-				$share->setSendPasswordByTalk(true);
1369
-			} elseif ($sendPasswordByTalk !== null) {
1370
-				$share->setSendPasswordByTalk(false);
1371
-			}
1372
-
1373
-			if ($token !== null) {
1374
-				if (!$this->shareManager->allowCustomTokens()) {
1375
-					throw new OCSForbiddenException($this->l->t('Custom share link tokens have been disabled by the administrator'));
1376
-				}
1377
-				if (!$this->validateToken($token)) {
1378
-					throw new OCSBadRequestException($this->l->t('Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen'));
1379
-				}
1380
-				$share->setToken($token);
1381
-			}
1382
-		}
1383
-
1384
-		// NOT A LINK SHARE
1385
-		else {
1386
-			if ($permissions !== null) {
1387
-				$share->setPermissions($permissions);
1388
-			}
1389
-		}
1390
-
1391
-		if ($expireDate === '') {
1392
-			$share->setExpirationDate(null);
1393
-		} elseif ($expireDate !== null) {
1394
-			try {
1395
-				$expireDateTime = $this->parseDate($expireDate);
1396
-				$share->setExpirationDate($expireDateTime);
1397
-			} catch (\Exception $e) {
1398
-				throw new OCSBadRequestException($e->getMessage(), $e);
1399
-			}
1400
-		}
1401
-
1402
-		try {
1403
-			$this->checkInheritedAttributes($share);
1404
-			$share = $this->shareManager->updateShare($share);
1405
-		} catch (HintException $e) {
1406
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1407
-			throw new OCSException($e->getHint(), (int)$code);
1408
-		} catch (\Exception $e) {
1409
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
1410
-			throw new OCSBadRequestException('Failed to update share.', $e);
1411
-		}
1412
-
1413
-		return new DataResponse($this->formatShare($share));
1414
-	}
1415
-
1416
-	private function validateToken(string $token): bool {
1417
-		if (mb_strlen($token) === 0) {
1418
-			return false;
1419
-		}
1420
-		if (!preg_match('/^[a-z0-9-]+$/i', $token)) {
1421
-			return false;
1422
-		}
1423
-		return true;
1424
-	}
1425
-
1426
-	/**
1427
-	 * Get all shares that are still pending
1428
-	 *
1429
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1430
-	 *
1431
-	 * 200: Pending shares returned
1432
-	 */
1433
-	#[NoAdminRequired]
1434
-	public function pendingShares(): DataResponse {
1435
-		$pendingShares = [];
1436
-
1437
-		$shareTypes = [
1438
-			IShare::TYPE_USER,
1439
-			IShare::TYPE_GROUP
1440
-		];
1441
-
1442
-		foreach ($shareTypes as $shareType) {
1443
-			$shares = $this->shareManager->getSharedWith($this->userId, $shareType, null, -1, 0);
1444
-
1445
-			foreach ($shares as $share) {
1446
-				if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1447
-					$pendingShares[] = $share;
1448
-				}
1449
-			}
1450
-		}
1451
-
1452
-		$result = array_values(array_filter(array_map(function (IShare $share) {
1453
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1454
-			$node = $userFolder->getFirstNodeById($share->getNodeId());
1455
-			if (!$node) {
1456
-				// fallback to guessing the path
1457
-				$node = $userFolder->get($share->getTarget());
1458
-				if ($node === null || $share->getTarget() === '') {
1459
-					return null;
1460
-				}
1461
-			}
1462
-
1463
-			try {
1464
-				$formattedShare = $this->formatShare($share, $node);
1465
-				$formattedShare['path'] = '/' . $share->getNode()->getName();
1466
-				$formattedShare['permissions'] = 0;
1467
-				return $formattedShare;
1468
-			} catch (NotFoundException $e) {
1469
-				return null;
1470
-			}
1471
-		}, $pendingShares), function ($entry) {
1472
-			return $entry !== null;
1473
-		}));
1474
-
1475
-		return new DataResponse($result);
1476
-	}
1477
-
1478
-	/**
1479
-	 * Accept a share
1480
-	 *
1481
-	 * @param string $id ID of the share
1482
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1483
-	 * @throws OCSNotFoundException Share not found
1484
-	 * @throws OCSException
1485
-	 * @throws OCSBadRequestException Share could not be accepted
1486
-	 *
1487
-	 * 200: Share accepted successfully
1488
-	 */
1489
-	#[NoAdminRequired]
1490
-	public function acceptShare(string $id): DataResponse {
1491
-		try {
1492
-			$share = $this->getShareById($id);
1493
-		} catch (ShareNotFound $e) {
1494
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1495
-		}
1496
-
1497
-		if (!$this->canAccessShare($share)) {
1498
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1499
-		}
1500
-
1501
-		try {
1502
-			$this->shareManager->acceptShare($share, $this->userId);
1503
-		} catch (HintException $e) {
1504
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1505
-			throw new OCSException($e->getHint(), (int)$code);
1506
-		} catch (\Exception $e) {
1507
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
1508
-			throw new OCSBadRequestException('Failed to accept share.', $e);
1509
-		}
1510
-
1511
-		return new DataResponse();
1512
-	}
1513
-
1514
-	/**
1515
-	 * Does the user have read permission on the share
1516
-	 *
1517
-	 * @param IShare $share the share to check
1518
-	 * @param boolean $checkGroups check groups as well?
1519
-	 * @return boolean
1520
-	 * @throws NotFoundException
1521
-	 *
1522
-	 * @suppress PhanUndeclaredClassMethod
1523
-	 */
1524
-	protected function canAccessShare(IShare $share, bool $checkGroups = true): bool {
1525
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1526
-		if ($share->getPermissions() === 0) {
1527
-			return false;
1528
-		}
1529
-
1530
-		// Owner of the file and the sharer of the file can always get share
1531
-		if ($share->getShareOwner() === $this->userId
1532
-			|| $share->getSharedBy() === $this->userId) {
1533
-			return true;
1534
-		}
1535
-
1536
-		// If the share is shared with you, you can access it!
1537
-		if ($share->getShareType() === IShare::TYPE_USER
1538
-			&& $share->getSharedWith() === $this->userId) {
1539
-			return true;
1540
-		}
1541
-
1542
-		// Have reshare rights on the shared file/folder ?
1543
-		// Does the currentUser have access to the shared file?
1544
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1545
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1546
-		if ($file && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1547
-			return true;
1548
-		}
1549
-
1550
-		// If in the recipient group, you can see the share
1551
-		if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1552
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1553
-			$user = $this->userManager->get($this->userId);
1554
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1555
-				return true;
1556
-			}
1557
-		}
1558
-
1559
-		if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1560
-			// TODO: have a sanity check like above?
1561
-			return true;
1562
-		}
1563
-
1564
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1565
-			try {
1566
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1567
-			} catch (ContainerExceptionInterface $e) {
1568
-				return false;
1569
-			}
1570
-		}
1571
-
1572
-		if ($share->getShareType() === IShare::TYPE_DECK) {
1573
-			try {
1574
-				return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1575
-			} catch (ContainerExceptionInterface $e) {
1576
-				return false;
1577
-			}
1578
-		}
1579
-
1580
-		if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1581
-			try {
1582
-				return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1583
-			} catch (ContainerExceptionInterface $e) {
1584
-				return false;
1585
-			}
1586
-		}
1587
-
1588
-		return false;
1589
-	}
1590
-
1591
-	/**
1592
-	 * Does the user have edit permission on the share
1593
-	 *
1594
-	 * @param IShare $share the share to check
1595
-	 * @return boolean
1596
-	 */
1597
-	protected function canEditShare(IShare $share): bool {
1598
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1599
-		if ($share->getPermissions() === 0) {
1600
-			return false;
1601
-		}
1602
-
1603
-		// The owner of the file and the creator of the share
1604
-		// can always edit the share
1605
-		if ($share->getShareOwner() === $this->userId
1606
-			|| $share->getSharedBy() === $this->userId
1607
-		) {
1608
-			return true;
1609
-		}
1610
-
1611
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1612
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1613
-		if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1614
-			return true;
1615
-		}
1616
-
1617
-		//! we do NOT support some kind of `admin` in groups.
1618
-		//! You cannot edit shares shared to a group you're
1619
-		//! a member of if you're not the share owner or the file owner!
1620
-
1621
-		return false;
1622
-	}
1623
-
1624
-	/**
1625
-	 * Does the user have delete permission on the share
1626
-	 *
1627
-	 * @param IShare $share the share to check
1628
-	 * @return boolean
1629
-	 */
1630
-	protected function canDeleteShare(IShare $share): bool {
1631
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1632
-		if ($share->getPermissions() === 0) {
1633
-			return false;
1634
-		}
1635
-
1636
-		// if the user is the recipient, i can unshare
1637
-		// the share with self
1638
-		if ($share->getShareType() === IShare::TYPE_USER
1639
-			&& $share->getSharedWith() === $this->userId
1640
-		) {
1641
-			return true;
1642
-		}
1643
-
1644
-		// The owner of the file and the creator of the share
1645
-		// can always delete the share
1646
-		if ($share->getShareOwner() === $this->userId
1647
-			|| $share->getSharedBy() === $this->userId
1648
-		) {
1649
-			return true;
1650
-		}
1651
-
1652
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1653
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1654
-		if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1655
-			return true;
1656
-		}
1657
-
1658
-		return false;
1659
-	}
1660
-
1661
-	/**
1662
-	 * Does the user have delete permission on the share
1663
-	 * This differs from the canDeleteShare function as it only
1664
-	 * remove the share for the current user. It does NOT
1665
-	 * completely delete the share but only the mount point.
1666
-	 * It can then be restored from the deleted shares section.
1667
-	 *
1668
-	 * @param IShare $share the share to check
1669
-	 * @return boolean
1670
-	 *
1671
-	 * @suppress PhanUndeclaredClassMethod
1672
-	 */
1673
-	protected function canDeleteShareFromSelf(IShare $share): bool {
1674
-		if ($share->getShareType() !== IShare::TYPE_GROUP
1675
-			&& $share->getShareType() !== IShare::TYPE_ROOM
1676
-			&& $share->getShareType() !== IShare::TYPE_DECK
1677
-			&& $share->getShareType() !== IShare::TYPE_SCIENCEMESH
1678
-		) {
1679
-			return false;
1680
-		}
1681
-
1682
-		if ($share->getShareOwner() === $this->userId
1683
-			|| $share->getSharedBy() === $this->userId
1684
-		) {
1685
-			// Delete the whole share, not just for self
1686
-			return false;
1687
-		}
1688
-
1689
-		// If in the recipient group, you can delete the share from self
1690
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1691
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1692
-			$user = $this->userManager->get($this->userId);
1693
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1694
-				return true;
1695
-			}
1696
-		}
1697
-
1698
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1699
-			try {
1700
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1701
-			} catch (ContainerExceptionInterface $e) {
1702
-				return false;
1703
-			}
1704
-		}
1705
-
1706
-		if ($share->getShareType() === IShare::TYPE_DECK) {
1707
-			try {
1708
-				return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1709
-			} catch (ContainerExceptionInterface $e) {
1710
-				return false;
1711
-			}
1712
-		}
1713
-
1714
-		if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1715
-			try {
1716
-				return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1717
-			} catch (ContainerExceptionInterface $e) {
1718
-				return false;
1719
-			}
1720
-		}
1721
-
1722
-		return false;
1723
-	}
1724
-
1725
-	/**
1726
-	 * Make sure that the passed date is valid ISO 8601
1727
-	 * So YYYY-MM-DD
1728
-	 * If not throw an exception
1729
-	 *
1730
-	 * @param string $expireDate
1731
-	 *
1732
-	 * @throws \Exception
1733
-	 * @return \DateTime
1734
-	 */
1735
-	private function parseDate(string $expireDate): \DateTime {
1736
-		try {
1737
-			$date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
1738
-			// Make sure it expires at midnight in owner timezone
1739
-			$date->setTime(0, 0, 0);
1740
-		} catch (\Exception $e) {
1741
-			throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
1742
-		}
1743
-
1744
-		return $date;
1745
-	}
1746
-
1747
-	/**
1748
-	 * Since we have multiple providers but the OCS Share API v1 does
1749
-	 * not support this we need to check all backends.
1750
-	 *
1751
-	 * @param string $id
1752
-	 * @return IShare
1753
-	 * @throws ShareNotFound
1754
-	 */
1755
-	private function getShareById(string $id): IShare {
1756
-		$providers = [
1757
-			'ocinternal' => null, // No type check needed
1758
-			'ocCircleShare' => IShare::TYPE_CIRCLE,
1759
-			'ocMailShare' => IShare::TYPE_EMAIL,
1760
-			'ocRoomShare' => null,
1761
-			'deck' => IShare::TYPE_DECK,
1762
-			'sciencemesh' => IShare::TYPE_SCIENCEMESH,
1763
-		];
1764
-
1765
-		// Add federated sharing as a provider only if it's allowed
1766
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1767
-			$providers['ocFederatedSharing'] = null; // No type check needed
1768
-		}
1769
-
1770
-		foreach ($providers as $prefix => $type) {
1771
-			try {
1772
-				if ($type === null || $this->shareManager->shareProviderExists($type)) {
1773
-					return $this->shareManager->getShareById($prefix . ':' . $id, $this->userId);
1774
-				}
1775
-			} catch (ShareNotFound $e) {
1776
-				// Do nothing, continue to next provider
1777
-			} catch (\Exception $e) {
1778
-				$this->logger->warning('Unexpected error in share provider', [
1779
-					'shareId' => $id,
1780
-					'provider' => $prefix,
1781
-					'exception' => $e,
1782
-				]);
1783
-			}
1784
-		}
1785
-		throw new ShareNotFound();
1786
-	}
1787
-
1788
-	/**
1789
-	 * Lock a Node
1790
-	 *
1791
-	 * @param Node $node
1792
-	 * @throws LockedException
1793
-	 */
1794
-	private function lock(Node $node) {
1795
-		$node->lock(ILockingProvider::LOCK_SHARED);
1796
-		$this->lockedNode = $node;
1797
-	}
1798
-
1799
-	/**
1800
-	 * Cleanup the remaining locks
1801
-	 * @throws LockedException
1802
-	 */
1803
-	public function cleanup() {
1804
-		if ($this->lockedNode !== null) {
1805
-			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1806
-		}
1807
-	}
1808
-
1809
-	/**
1810
-	 * Returns the helper of ShareAPIController for room shares.
1811
-	 *
1812
-	 * If the Talk application is not enabled or the helper is not available
1813
-	 * a ContainerExceptionInterface is thrown instead.
1814
-	 *
1815
-	 * @return \OCA\Talk\Share\Helper\ShareAPIController
1816
-	 * @throws ContainerExceptionInterface
1817
-	 */
1818
-	private function getRoomShareHelper() {
1819
-		if (!$this->appManager->isEnabledForUser('spreed')) {
1820
-			throw new QueryException();
1821
-		}
1822
-
1823
-		return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
1824
-	}
1825
-
1826
-	/**
1827
-	 * Returns the helper of ShareAPIHelper for deck shares.
1828
-	 *
1829
-	 * If the Deck application is not enabled or the helper is not available
1830
-	 * a ContainerExceptionInterface is thrown instead.
1831
-	 *
1832
-	 * @return ShareAPIHelper
1833
-	 * @throws ContainerExceptionInterface
1834
-	 */
1835
-	private function getDeckShareHelper() {
1836
-		if (!$this->appManager->isEnabledForUser('deck')) {
1837
-			throw new QueryException();
1838
-		}
1839
-
1840
-		return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
1841
-	}
1842
-
1843
-	/**
1844
-	 * Returns the helper of ShareAPIHelper for sciencemesh shares.
1845
-	 *
1846
-	 * If the sciencemesh application is not enabled or the helper is not available
1847
-	 * a ContainerExceptionInterface is thrown instead.
1848
-	 *
1849
-	 * @return ShareAPIHelper
1850
-	 * @throws ContainerExceptionInterface
1851
-	 */
1852
-	private function getSciencemeshShareHelper() {
1853
-		if (!$this->appManager->isEnabledForUser('sciencemesh')) {
1854
-			throw new QueryException();
1855
-		}
1856
-
1857
-		return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
1858
-	}
1859
-
1860
-	/**
1861
-	 * @param string $viewer
1862
-	 * @param Node $node
1863
-	 * @param bool $reShares
1864
-	 *
1865
-	 * @return IShare[]
1866
-	 */
1867
-	private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1868
-		$providers = [
1869
-			IShare::TYPE_USER,
1870
-			IShare::TYPE_GROUP,
1871
-			IShare::TYPE_LINK,
1872
-			IShare::TYPE_EMAIL,
1873
-			IShare::TYPE_CIRCLE,
1874
-			IShare::TYPE_ROOM,
1875
-			IShare::TYPE_DECK,
1876
-			IShare::TYPE_SCIENCEMESH
1877
-		];
1878
-
1879
-		// Should we assume that the (currentUser) viewer is the owner of the node !?
1880
-		$shares = [];
1881
-		foreach ($providers as $provider) {
1882
-			if (!$this->shareManager->shareProviderExists($provider)) {
1883
-				continue;
1884
-			}
1885
-
1886
-			$providerShares
1887
-				= $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1888
-			$shares = array_merge($shares, $providerShares);
1889
-		}
1890
-
1891
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1892
-			$federatedShares = $this->shareManager->getSharesBy(
1893
-				$this->userId, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1894
-			);
1895
-			$shares = array_merge($shares, $federatedShares);
1896
-		}
1897
-
1898
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1899
-			$federatedShares = $this->shareManager->getSharesBy(
1900
-				$this->userId, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1901
-			);
1902
-			$shares = array_merge($shares, $federatedShares);
1903
-		}
1904
-
1905
-		return $shares;
1906
-	}
1907
-
1908
-
1909
-	/**
1910
-	 * @param Node $node
1911
-	 *
1912
-	 * @throws SharingRightsException
1913
-	 */
1914
-	private function confirmSharingRights(Node $node): void {
1915
-		if (!$this->hasResharingRights($this->userId, $node)) {
1916
-			throw new SharingRightsException($this->l->t('No sharing rights on this item'));
1917
-		}
1918
-	}
1919
-
1920
-
1921
-	/**
1922
-	 * @param string $viewer
1923
-	 * @param Node $node
1924
-	 *
1925
-	 * @return bool
1926
-	 */
1927
-	private function hasResharingRights($viewer, $node): bool {
1928
-		if ($viewer === $node->getOwner()->getUID()) {
1929
-			return true;
1930
-		}
1931
-
1932
-		foreach ([$node, $node->getParent()] as $node) {
1933
-			$shares = $this->getSharesFromNode($viewer, $node, true);
1934
-			foreach ($shares as $share) {
1935
-				try {
1936
-					if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1937
-						return true;
1938
-					}
1939
-				} catch (InvalidPathException|NotFoundException $e) {
1940
-				}
1941
-			}
1942
-		}
1943
-
1944
-		return false;
1945
-	}
1946
-
1947
-
1948
-	/**
1949
-	 * Returns if we can find resharing rights in an IShare object for a specific user.
1950
-	 *
1951
-	 * @suppress PhanUndeclaredClassMethod
1952
-	 *
1953
-	 * @param string $userId
1954
-	 * @param IShare $share
1955
-	 * @param Node $node
1956
-	 *
1957
-	 * @return bool
1958
-	 * @throws NotFoundException
1959
-	 * @throws InvalidPathException
1960
-	 */
1961
-	private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1962
-		if ($share->getShareOwner() === $userId) {
1963
-			return true;
1964
-		}
1965
-
1966
-		// we check that current user have parent resharing rights on the current file
1967
-		if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1968
-			return true;
1969
-		}
1970
-
1971
-		if ((Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1972
-			return false;
1973
-		}
1974
-
1975
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1976
-			return true;
1977
-		}
1978
-
1979
-		if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1980
-			return true;
1981
-		}
1982
-
1983
-		if ($share->getShareType() === IShare::TYPE_CIRCLE && Server::get(IAppManager::class)->isEnabledForUser('circles')
1984
-			&& class_exists('\OCA\Circles\Api\v1\Circles')) {
1985
-			$hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
1986
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1987
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1988
-			if ($shareWithLength === false) {
1989
-				$sharedWith = substr($share->getSharedWith(), $shareWithStart);
1990
-			} else {
1991
-				$sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1992
-			}
1993
-			try {
1994
-				$member = Circles::getMember($sharedWith, $userId, 1);
1995
-				if ($member->getLevel() >= 4) {
1996
-					return true;
1997
-				}
1998
-				return false;
1999
-			} catch (ContainerExceptionInterface $e) {
2000
-				return false;
2001
-			}
2002
-		}
2003
-
2004
-		return false;
2005
-	}
2006
-
2007
-	/**
2008
-	 * Get all the shares for the current user
2009
-	 *
2010
-	 * @param Node|null $path
2011
-	 * @param boolean $reshares
2012
-	 * @return IShare[]
2013
-	 */
2014
-	private function getAllShares(?Node $path = null, bool $reshares = false) {
2015
-		// Get all shares
2016
-		$userShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_USER, $path, $reshares, -1, 0);
2017
-		$groupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
2018
-		$linkShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_LINK, $path, $reshares, -1, 0);
2019
-
2020
-		// EMAIL SHARES
2021
-		$mailShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
2022
-
2023
-		// TEAM SHARES
2024
-		$circleShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
2025
-
2026
-		// TALK SHARES
2027
-		$roomShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
2028
-
2029
-		// DECK SHARES
2030
-		$deckShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_DECK, $path, $reshares, -1, 0);
2031
-
2032
-		// SCIENCEMESH SHARES
2033
-		$sciencemeshShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_SCIENCEMESH, $path, $reshares, -1, 0);
2034
-
2035
-		// FEDERATION
2036
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
2037
-			$federatedShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
2038
-		} else {
2039
-			$federatedShares = [];
2040
-		}
2041
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
2042
-			$federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
2043
-		} else {
2044
-			$federatedGroupShares = [];
2045
-		}
2046
-
2047
-		return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $federatedShares, $federatedGroupShares);
2048
-	}
2049
-
2050
-
2051
-	/**
2052
-	 * merging already formatted shares.
2053
-	 * We'll make an associative array to easily detect duplicate Ids.
2054
-	 * Keys _needs_ to be removed after all shares are retrieved and merged.
2055
-	 *
2056
-	 * @param array $shares
2057
-	 * @param array $newShares
2058
-	 */
2059
-	private function mergeFormattedShares(array &$shares, array $newShares) {
2060
-		foreach ($newShares as $newShare) {
2061
-			if (!array_key_exists($newShare['id'], $shares)) {
2062
-				$shares[$newShare['id']] = $newShare;
2063
-			}
2064
-		}
2065
-	}
2066
-
2067
-	/**
2068
-	 * @param IShare $share
2069
-	 * @param string|null $attributesString
2070
-	 * @return IShare modified share
2071
-	 */
2072
-	private function setShareAttributes(IShare $share, ?string $attributesString) {
2073
-		$newShareAttributes = null;
2074
-		if ($attributesString !== null) {
2075
-			$newShareAttributes = $this->shareManager->newShare()->newAttributes();
2076
-			$formattedShareAttributes = \json_decode($attributesString, true);
2077
-			if (is_array($formattedShareAttributes)) {
2078
-				foreach ($formattedShareAttributes as $formattedAttr) {
2079
-					$newShareAttributes->setAttribute(
2080
-						$formattedAttr['scope'],
2081
-						$formattedAttr['key'],
2082
-						$formattedAttr['value'],
2083
-					);
2084
-				}
2085
-			} else {
2086
-				throw new OCSBadRequestException($this->l->t('Invalid share attributes provided: "%s"', [$attributesString]));
2087
-			}
2088
-		}
2089
-		$share->setAttributes($newShareAttributes);
2090
-
2091
-		return $share;
2092
-	}
2093
-
2094
-	private function checkInheritedAttributes(IShare $share): void {
2095
-		if (!$share->getSharedBy()) {
2096
-			return; // Probably in a test
2097
-		}
2098
-
2099
-		$canDownload = false;
2100
-		$hideDownload = true;
2101
-		$userExplicitlySetHideDownload = $share->getHideDownload(); // Capture user's explicit choice
2102
-
2103
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
2104
-		$nodes = $userFolder->getById($share->getNodeId());
2105
-		foreach ($nodes as $node) {
2106
-			// Owner always can download it - so allow it, but respect their explicit choice about hiding downloads
2107
-			if ($node->getOwner()?->getUID() === $share->getSharedBy()) {
2108
-				$canDownload = true;
2109
-				$hideDownload = $userExplicitlySetHideDownload;
2110
-				break;
2111
-			}
2112
-
2113
-			if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
2114
-				$storage = $node->getStorage();
2115
-				if ($storage instanceof Wrapper) {
2116
-					$storage = $storage->getInstanceOfStorage(SharedStorage::class);
2117
-					if ($storage === null) {
2118
-						throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
2119
-					}
2120
-				} else {
2121
-					throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
2122
-				}
2123
-
2124
-				/** @var SharedStorage $storage */
2125
-				$originalShare = $storage->getShare();
2126
-				$inheritedAttributes = $originalShare->getAttributes();
2127
-
2128
-				// For federated shares: users can only be MORE restrictive, never LESS restrictive
2129
-				// If parent has hideDownload=true, child MUST have hideDownload=true
2130
-				$parentHidesDownload = $originalShare->getHideDownload();
2131
-
2132
-				// Check if download permission is available from parent
2133
-				$parentAllowsDownload = $inheritedAttributes === null || $inheritedAttributes->getAttribute('permissions', 'download') !== false;
2134
-
2135
-				// Apply inheritance rules:
2136
-				// 1. If parent hides download, child must hide download
2137
-				// 2. If parent allows download, child can choose to hide or allow
2138
-				// 3. If parent forbids download, child cannot allow download
2139
-				$hideDownload = $parentHidesDownload || $userExplicitlySetHideDownload;
2140
-
2141
-				$canDownload = $canDownload || $parentAllowsDownload;
2142
-
2143
-			} elseif ($node->getStorage()->instanceOfStorage(Storage::class)) {
2144
-				$canDownload = true; // in case of federation storage, we can expect the download to be activated by default
2145
-				// For external federation storage, respect user's choice if downloads are available
2146
-				$hideDownload = $userExplicitlySetHideDownload;
2147
-			}
2148
-		}
2149
-
2150
-		// Apply the final restrictions:
2151
-		// 1. If parent doesn't allow downloads at all, force hide and disable download attribute
2152
-		// 2. If parent allows downloads, respect user's hideDownload choice
2153
-		if (!$canDownload) {
2154
-			// Parent completely forbids downloads - must enforce this restriction
2155
-			$share->setHideDownload(true);
2156
-			$attributes = $share->getAttributes() ?? $share->newAttributes();
2157
-			$attributes->setAttribute('permissions', 'download', false);
2158
-			$share->setAttributes($attributes);
2159
-		} elseif ($hideDownload) {
2160
-			// Either parent forces hide, or user chooses to hide - respect this
2161
-			$share->setHideDownload(true);
2162
-		} else {
2163
-			// User explicitly wants to allow downloads and parent permits it
2164
-			$share->setHideDownload(false);
2165
-		}
2166
-	}
2167
-
2168
-	/**
2169
-	 * Send a mail notification again for a share.
2170
-	 * The mail_send option must be enabled for the given share.
2171
-	 * @param string $id the share ID
2172
-	 * @param string $password the password to check against. Necessary for password protected shares.
2173
-	 * @throws OCSNotFoundException Share not found
2174
-	 * @throws OCSForbiddenException You are not allowed to send mail notifications
2175
-	 * @throws OCSBadRequestException Invalid request or wrong password
2176
-	 * @throws OCSException Error while sending mail notification
2177
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
2178
-	 *
2179
-	 * 200: The email notification was sent successfully
2180
-	 */
2181
-	#[NoAdminRequired]
2182
-	#[UserRateLimit(limit: 10, period: 600)]
2183
-	public function sendShareEmail(string $id, $password = ''): DataResponse {
2184
-		try {
2185
-			$share = $this->getShareById($id);
2186
-
2187
-			if (!$this->canAccessShare($share, false)) {
2188
-				throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2189
-			}
2190
-
2191
-			if (!$this->canEditShare($share)) {
2192
-				throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2193
-			}
2194
-
2195
-			// For mail and link shares, the user must be
2196
-			// the owner of the share, not only the file owner.
2197
-			if ($share->getShareType() === IShare::TYPE_EMAIL
2198
-				|| $share->getShareType() === IShare::TYPE_LINK) {
2199
-				if ($share->getSharedBy() !== $this->userId) {
2200
-					throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2201
-				}
2202
-			}
2203
-
2204
-			try {
2205
-				$provider = $this->factory->getProviderForType($share->getShareType());
2206
-				if (!($provider instanceof IShareProviderWithNotification)) {
2207
-					throw new OCSBadRequestException($this->l->t('No mail notification configured for this share type'));
2208
-				}
2209
-
2210
-				// Circumvent the password encrypted data by
2211
-				// setting the password clear. We're not storing
2212
-				// the password clear, it is just a temporary
2213
-				// object manipulation. The password will stay
2214
-				// encrypted in the database.
2215
-				if ($share->getPassword() !== null && $share->getPassword() !== $password) {
2216
-					if (!$this->shareManager->checkPassword($share, $password)) {
2217
-						throw new OCSBadRequestException($this->l->t('Wrong password'));
2218
-					}
2219
-					$share = $share->setPassword($password);
2220
-				}
2221
-
2222
-				$provider->sendMailNotification($share);
2223
-				return new DataResponse();
2224
-			} catch (Exception $e) {
2225
-				$this->logger->error($e->getMessage(), ['exception' => $e]);
2226
-				throw new OCSException($this->l->t('Error while sending mail notification'));
2227
-			}
2228
-
2229
-		} catch (ShareNotFound $e) {
2230
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2231
-		}
2232
-	}
2233
-
2234
-	/**
2235
-	 * Get a unique share token
2236
-	 *
2237
-	 * @throws OCSException Failed to generate a unique token
2238
-	 *
2239
-	 * @return DataResponse<Http::STATUS_OK, array{token: string}, array{}>
2240
-	 *
2241
-	 * 200: Token generated successfully
2242
-	 */
2243
-	#[ApiRoute(verb: 'GET', url: '/api/v1/token')]
2244
-	#[NoAdminRequired]
2245
-	public function generateToken(): DataResponse {
2246
-		try {
2247
-			$token = $this->shareManager->generateToken();
2248
-			return new DataResponse([
2249
-				'token' => $token,
2250
-			]);
2251
-		} catch (ShareTokenException $e) {
2252
-			throw new OCSException($this->l->t('Failed to generate a unique token'));
2253
-		}
2254
-	}
2255
-
2256
-	/**
2257
-	 * Populate the result set with file tags
2258
-	 *
2259
-	 * @psalm-template T of array{tags?: list<string>, file_source: int, ...array<string, mixed>}
2260
-	 * @param list<T> $fileList
2261
-	 * @return list<T> file list populated with tags
2262
-	 */
2263
-	private function populateTags(array $fileList): array {
2264
-		$tagger = $this->tagManager->load('files');
2265
-		$tags = $tagger->getTagsForObjects(array_map(static fn (array $fileData) => $fileData['file_source'], $fileList));
2266
-
2267
-		if (!is_array($tags)) {
2268
-			throw new \UnexpectedValueException('$tags must be an array');
2269
-		}
2270
-
2271
-		// Set empty tag array
2272
-		foreach ($fileList as &$fileData) {
2273
-			$fileData['tags'] = [];
2274
-		}
2275
-		unset($fileData);
2276
-
2277
-		if (!empty($tags)) {
2278
-			foreach ($tags as $fileId => $fileTags) {
2279
-				foreach ($fileList as &$fileData) {
2280
-					if ($fileId !== $fileData['file_source']) {
2281
-						continue;
2282
-					}
2283
-
2284
-					$fileData['tags'] = $fileTags;
2285
-				}
2286
-				unset($fileData);
2287
-			}
2288
-		}
2289
-
2290
-		return $fileList;
2291
-	}
1102
+                continue;
1103
+            }
1104
+
1105
+            if (in_array($share->getId(), $known)
1106
+                || ($share->getSharedWith() === $this->userId && $share->getShareType() === IShare::TYPE_USER)) {
1107
+                continue;
1108
+            }
1109
+
1110
+            $known[] = $share->getId();
1111
+            try {
1112
+                /** @var IShare $share */
1113
+                $format = $this->formatShare($share, $node);
1114
+                $formatted[] = $format;
1115
+
1116
+                // let's also build a list of shares created
1117
+                // by the current user only, in case
1118
+                // there is no resharing rights
1119
+                if ($share->getSharedBy() === $this->userId) {
1120
+                    $miniFormatted[] = $format;
1121
+                }
1122
+
1123
+                // check if one of those share is shared with me
1124
+                // and if I have resharing rights on it
1125
+                if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $node)) {
1126
+                    $resharingRight = true;
1127
+                }
1128
+            } catch (InvalidPathException|NotFoundException $e) {
1129
+            }
1130
+        }
1131
+
1132
+        if (!$resharingRight) {
1133
+            $formatted = $miniFormatted;
1134
+        }
1135
+
1136
+        // fix eventual missing display name from federated shares
1137
+        $formatted = $this->fixMissingDisplayName($formatted);
1138
+
1139
+        if ($includeTags) {
1140
+            $formatted = $this->populateTags($formatted);
1141
+        }
1142
+
1143
+        return $formatted;
1144
+    }
1145
+
1146
+
1147
+    /**
1148
+     * Get all shares relative to a file, including parent folders shares rights
1149
+     *
1150
+     * @param string $path Path all shares will be relative to
1151
+     *
1152
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1153
+     * @throws InvalidPathException
1154
+     * @throws NotFoundException
1155
+     * @throws OCSNotFoundException The given path is invalid
1156
+     * @throws SharingRightsException
1157
+     *
1158
+     * 200: Shares returned
1159
+     */
1160
+    #[NoAdminRequired]
1161
+    public function getInheritedShares(string $path): DataResponse {
1162
+        // get Node from (string) path.
1163
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1164
+        try {
1165
+            $node = $userFolder->get($path);
1166
+            $this->lock($node);
1167
+        } catch (NotFoundException $e) {
1168
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
1169
+        } catch (LockedException $e) {
1170
+            throw new OCSNotFoundException($this->l->t('Could not lock path'));
1171
+        }
1172
+
1173
+        if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
1174
+            throw new SharingRightsException($this->l->t('no sharing rights on this item'));
1175
+        }
1176
+
1177
+        // The current top parent we have access to
1178
+        $parent = $node;
1179
+
1180
+        // initiate real owner.
1181
+        $owner = $node->getOwner()
1182
+            ->getUID();
1183
+        if (!$this->userManager->userExists($owner)) {
1184
+            return new DataResponse([]);
1185
+        }
1186
+
1187
+        // get node based on the owner, fix owner in case of external storage
1188
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1189
+        if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
1190
+            $owner = $node->getOwner()
1191
+                ->getUID();
1192
+            $userFolder = $this->rootFolder->getUserFolder($owner);
1193
+            $node = $userFolder->getFirstNodeById($node->getId());
1194
+        }
1195
+        $basePath = $userFolder->getPath();
1196
+
1197
+        // generate node list for each parent folders
1198
+        /** @var Node[] $nodes */
1199
+        $nodes = [];
1200
+        while (true) {
1201
+            $node = $node->getParent();
1202
+            if ($node->getPath() === $basePath) {
1203
+                break;
1204
+            }
1205
+            $nodes[] = $node;
1206
+        }
1207
+
1208
+        // The user that is requesting this list
1209
+        $currentUserFolder = $this->rootFolder->getUserFolder($this->userId);
1210
+
1211
+        // for each nodes, retrieve shares.
1212
+        $shares = [];
1213
+
1214
+        foreach ($nodes as $node) {
1215
+            $getShares = $this->getFormattedShares($owner, $node, false, true);
1216
+
1217
+            $currentUserNode = $currentUserFolder->getFirstNodeById($node->getId());
1218
+            if ($currentUserNode) {
1219
+                $parent = $currentUserNode;
1220
+            }
1221
+
1222
+            $subPath = $currentUserFolder->getRelativePath($parent->getPath());
1223
+            foreach ($getShares as &$share) {
1224
+                $share['via_fileid'] = $parent->getId();
1225
+                $share['via_path'] = $subPath;
1226
+            }
1227
+            $this->mergeFormattedShares($shares, $getShares);
1228
+        }
1229
+
1230
+        return new DataResponse(array_values($shares));
1231
+    }
1232
+
1233
+    /**
1234
+     * Check whether a set of permissions contains the permissions to check.
1235
+     */
1236
+    private function hasPermission(int $permissionsSet, int $permissionsToCheck): bool {
1237
+        return ($permissionsSet & $permissionsToCheck) === $permissionsToCheck;
1238
+    }
1239
+
1240
+    /**
1241
+     * Update a share
1242
+     *
1243
+     * @param string $id ID of the share
1244
+     * @param int|null $permissions New permissions
1245
+     * @param string|null $password New password
1246
+     * @param string|null $sendPasswordByTalk New condition if the password should be send over Talk
1247
+     * @param string|null $publicUpload New condition if public uploading is allowed
1248
+     * @param string|null $expireDate New expiry date
1249
+     * @param string|null $note New note
1250
+     * @param string|null $label New label
1251
+     * @param string|null $hideDownload New condition if the download should be hidden
1252
+     * @param string|null $attributes New additional attributes
1253
+     * @param string|null $sendMail if the share should be send by mail.
1254
+     *                              Considering the share already exists, no mail will be send after the share is updated.
1255
+     *                              You will have to use the sendMail action to send the mail.
1256
+     * @param string|null $shareWith New recipient for email shares
1257
+     * @param string|null $token New token
1258
+     * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
1259
+     * @throws OCSBadRequestException Share could not be updated because the requested changes are invalid
1260
+     * @throws OCSForbiddenException Missing permissions to update the share
1261
+     * @throws OCSNotFoundException Share not found
1262
+     *
1263
+     * 200: Share updated successfully
1264
+     */
1265
+    #[NoAdminRequired]
1266
+    public function updateShare(
1267
+        string $id,
1268
+        ?int $permissions = null,
1269
+        ?string $password = null,
1270
+        ?string $sendPasswordByTalk = null,
1271
+        ?string $publicUpload = null,
1272
+        ?string $expireDate = null,
1273
+        ?string $note = null,
1274
+        ?string $label = null,
1275
+        ?string $hideDownload = null,
1276
+        ?string $attributes = null,
1277
+        ?string $sendMail = null,
1278
+        ?string $token = null,
1279
+    ): DataResponse {
1280
+        try {
1281
+            $share = $this->getShareById($id);
1282
+        } catch (ShareNotFound $e) {
1283
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1284
+        }
1285
+
1286
+        $this->lock($share->getNode());
1287
+
1288
+        if (!$this->canAccessShare($share, false)) {
1289
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1290
+        }
1291
+
1292
+        if (!$this->canEditShare($share)) {
1293
+            throw new OCSForbiddenException($this->l->t('You are not allowed to edit incoming shares'));
1294
+        }
1295
+
1296
+        if (
1297
+            $permissions === null
1298
+            && $password === null
1299
+            && $sendPasswordByTalk === null
1300
+            && $publicUpload === null
1301
+            && $expireDate === null
1302
+            && $note === null
1303
+            && $label === null
1304
+            && $hideDownload === null
1305
+            && $attributes === null
1306
+            && $sendMail === null
1307
+            && $token === null
1308
+        ) {
1309
+            throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
1310
+        }
1311
+
1312
+        if ($note !== null) {
1313
+            $share->setNote($note);
1314
+        }
1315
+
1316
+        if ($attributes !== null) {
1317
+            $share = $this->setShareAttributes($share, $attributes);
1318
+        }
1319
+
1320
+        // Handle mail send
1321
+        if ($sendMail === 'true' || $sendMail === 'false') {
1322
+            $share->setMailSend($sendMail === 'true');
1323
+        }
1324
+
1325
+        /**
1326
+         * expiration date, password and publicUpload only make sense for link shares
1327
+         */
1328
+        if ($share->getShareType() === IShare::TYPE_LINK
1329
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1330
+
1331
+            // Update hide download state
1332
+            if ($hideDownload === 'true') {
1333
+                $share->setHideDownload(true);
1334
+            } elseif ($hideDownload === 'false') {
1335
+                $share->setHideDownload(false);
1336
+            }
1337
+
1338
+            // If either manual permissions are specified or publicUpload
1339
+            // then we need to also update the permissions of the share
1340
+            if ($permissions !== null || $publicUpload !== null) {
1341
+                $hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
1342
+                $permissions = $this->getLinkSharePermissions($permissions ?? Constants::PERMISSION_READ, $hasPublicUpload);
1343
+                $this->validateLinkSharePermissions($share->getNode(), $permissions, $hasPublicUpload);
1344
+                $share->setPermissions($permissions);
1345
+            }
1346
+
1347
+            $passwordParamSent = $password !== null;
1348
+            if ($passwordParamSent) {
1349
+                if ($password === '') {
1350
+                    $share->setPassword(null);
1351
+                } else {
1352
+                    $share->setPassword($password);
1353
+                }
1354
+            }
1355
+
1356
+            if ($label !== null) {
1357
+                if (strlen($label) > 255) {
1358
+                    throw new OCSBadRequestException('Maximum label length is 255');
1359
+                }
1360
+                $share->setLabel($label);
1361
+            }
1362
+
1363
+            if ($sendPasswordByTalk === 'true') {
1364
+                if (!$this->appManager->isEnabledForUser('spreed')) {
1365
+                    throw new OCSForbiddenException($this->l->t('"Sending the password by Nextcloud Talk" for sharing a file or folder failed because Nextcloud Talk is not enabled.'));
1366
+                }
1367
+
1368
+                $share->setSendPasswordByTalk(true);
1369
+            } elseif ($sendPasswordByTalk !== null) {
1370
+                $share->setSendPasswordByTalk(false);
1371
+            }
1372
+
1373
+            if ($token !== null) {
1374
+                if (!$this->shareManager->allowCustomTokens()) {
1375
+                    throw new OCSForbiddenException($this->l->t('Custom share link tokens have been disabled by the administrator'));
1376
+                }
1377
+                if (!$this->validateToken($token)) {
1378
+                    throw new OCSBadRequestException($this->l->t('Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen'));
1379
+                }
1380
+                $share->setToken($token);
1381
+            }
1382
+        }
1383
+
1384
+        // NOT A LINK SHARE
1385
+        else {
1386
+            if ($permissions !== null) {
1387
+                $share->setPermissions($permissions);
1388
+            }
1389
+        }
1390
+
1391
+        if ($expireDate === '') {
1392
+            $share->setExpirationDate(null);
1393
+        } elseif ($expireDate !== null) {
1394
+            try {
1395
+                $expireDateTime = $this->parseDate($expireDate);
1396
+                $share->setExpirationDate($expireDateTime);
1397
+            } catch (\Exception $e) {
1398
+                throw new OCSBadRequestException($e->getMessage(), $e);
1399
+            }
1400
+        }
1401
+
1402
+        try {
1403
+            $this->checkInheritedAttributes($share);
1404
+            $share = $this->shareManager->updateShare($share);
1405
+        } catch (HintException $e) {
1406
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1407
+            throw new OCSException($e->getHint(), (int)$code);
1408
+        } catch (\Exception $e) {
1409
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
1410
+            throw new OCSBadRequestException('Failed to update share.', $e);
1411
+        }
1412
+
1413
+        return new DataResponse($this->formatShare($share));
1414
+    }
1415
+
1416
+    private function validateToken(string $token): bool {
1417
+        if (mb_strlen($token) === 0) {
1418
+            return false;
1419
+        }
1420
+        if (!preg_match('/^[a-z0-9-]+$/i', $token)) {
1421
+            return false;
1422
+        }
1423
+        return true;
1424
+    }
1425
+
1426
+    /**
1427
+     * Get all shares that are still pending
1428
+     *
1429
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1430
+     *
1431
+     * 200: Pending shares returned
1432
+     */
1433
+    #[NoAdminRequired]
1434
+    public function pendingShares(): DataResponse {
1435
+        $pendingShares = [];
1436
+
1437
+        $shareTypes = [
1438
+            IShare::TYPE_USER,
1439
+            IShare::TYPE_GROUP
1440
+        ];
1441
+
1442
+        foreach ($shareTypes as $shareType) {
1443
+            $shares = $this->shareManager->getSharedWith($this->userId, $shareType, null, -1, 0);
1444
+
1445
+            foreach ($shares as $share) {
1446
+                if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1447
+                    $pendingShares[] = $share;
1448
+                }
1449
+            }
1450
+        }
1451
+
1452
+        $result = array_values(array_filter(array_map(function (IShare $share) {
1453
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1454
+            $node = $userFolder->getFirstNodeById($share->getNodeId());
1455
+            if (!$node) {
1456
+                // fallback to guessing the path
1457
+                $node = $userFolder->get($share->getTarget());
1458
+                if ($node === null || $share->getTarget() === '') {
1459
+                    return null;
1460
+                }
1461
+            }
1462
+
1463
+            try {
1464
+                $formattedShare = $this->formatShare($share, $node);
1465
+                $formattedShare['path'] = '/' . $share->getNode()->getName();
1466
+                $formattedShare['permissions'] = 0;
1467
+                return $formattedShare;
1468
+            } catch (NotFoundException $e) {
1469
+                return null;
1470
+            }
1471
+        }, $pendingShares), function ($entry) {
1472
+            return $entry !== null;
1473
+        }));
1474
+
1475
+        return new DataResponse($result);
1476
+    }
1477
+
1478
+    /**
1479
+     * Accept a share
1480
+     *
1481
+     * @param string $id ID of the share
1482
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1483
+     * @throws OCSNotFoundException Share not found
1484
+     * @throws OCSException
1485
+     * @throws OCSBadRequestException Share could not be accepted
1486
+     *
1487
+     * 200: Share accepted successfully
1488
+     */
1489
+    #[NoAdminRequired]
1490
+    public function acceptShare(string $id): DataResponse {
1491
+        try {
1492
+            $share = $this->getShareById($id);
1493
+        } catch (ShareNotFound $e) {
1494
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1495
+        }
1496
+
1497
+        if (!$this->canAccessShare($share)) {
1498
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1499
+        }
1500
+
1501
+        try {
1502
+            $this->shareManager->acceptShare($share, $this->userId);
1503
+        } catch (HintException $e) {
1504
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1505
+            throw new OCSException($e->getHint(), (int)$code);
1506
+        } catch (\Exception $e) {
1507
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
1508
+            throw new OCSBadRequestException('Failed to accept share.', $e);
1509
+        }
1510
+
1511
+        return new DataResponse();
1512
+    }
1513
+
1514
+    /**
1515
+     * Does the user have read permission on the share
1516
+     *
1517
+     * @param IShare $share the share to check
1518
+     * @param boolean $checkGroups check groups as well?
1519
+     * @return boolean
1520
+     * @throws NotFoundException
1521
+     *
1522
+     * @suppress PhanUndeclaredClassMethod
1523
+     */
1524
+    protected function canAccessShare(IShare $share, bool $checkGroups = true): bool {
1525
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1526
+        if ($share->getPermissions() === 0) {
1527
+            return false;
1528
+        }
1529
+
1530
+        // Owner of the file and the sharer of the file can always get share
1531
+        if ($share->getShareOwner() === $this->userId
1532
+            || $share->getSharedBy() === $this->userId) {
1533
+            return true;
1534
+        }
1535
+
1536
+        // If the share is shared with you, you can access it!
1537
+        if ($share->getShareType() === IShare::TYPE_USER
1538
+            && $share->getSharedWith() === $this->userId) {
1539
+            return true;
1540
+        }
1541
+
1542
+        // Have reshare rights on the shared file/folder ?
1543
+        // Does the currentUser have access to the shared file?
1544
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1545
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1546
+        if ($file && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1547
+            return true;
1548
+        }
1549
+
1550
+        // If in the recipient group, you can see the share
1551
+        if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1552
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1553
+            $user = $this->userManager->get($this->userId);
1554
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1555
+                return true;
1556
+            }
1557
+        }
1558
+
1559
+        if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1560
+            // TODO: have a sanity check like above?
1561
+            return true;
1562
+        }
1563
+
1564
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1565
+            try {
1566
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1567
+            } catch (ContainerExceptionInterface $e) {
1568
+                return false;
1569
+            }
1570
+        }
1571
+
1572
+        if ($share->getShareType() === IShare::TYPE_DECK) {
1573
+            try {
1574
+                return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1575
+            } catch (ContainerExceptionInterface $e) {
1576
+                return false;
1577
+            }
1578
+        }
1579
+
1580
+        if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1581
+            try {
1582
+                return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1583
+            } catch (ContainerExceptionInterface $e) {
1584
+                return false;
1585
+            }
1586
+        }
1587
+
1588
+        return false;
1589
+    }
1590
+
1591
+    /**
1592
+     * Does the user have edit permission on the share
1593
+     *
1594
+     * @param IShare $share the share to check
1595
+     * @return boolean
1596
+     */
1597
+    protected function canEditShare(IShare $share): bool {
1598
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1599
+        if ($share->getPermissions() === 0) {
1600
+            return false;
1601
+        }
1602
+
1603
+        // The owner of the file and the creator of the share
1604
+        // can always edit the share
1605
+        if ($share->getShareOwner() === $this->userId
1606
+            || $share->getSharedBy() === $this->userId
1607
+        ) {
1608
+            return true;
1609
+        }
1610
+
1611
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1612
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1613
+        if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1614
+            return true;
1615
+        }
1616
+
1617
+        //! we do NOT support some kind of `admin` in groups.
1618
+        //! You cannot edit shares shared to a group you're
1619
+        //! a member of if you're not the share owner or the file owner!
1620
+
1621
+        return false;
1622
+    }
1623
+
1624
+    /**
1625
+     * Does the user have delete permission on the share
1626
+     *
1627
+     * @param IShare $share the share to check
1628
+     * @return boolean
1629
+     */
1630
+    protected function canDeleteShare(IShare $share): bool {
1631
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1632
+        if ($share->getPermissions() === 0) {
1633
+            return false;
1634
+        }
1635
+
1636
+        // if the user is the recipient, i can unshare
1637
+        // the share with self
1638
+        if ($share->getShareType() === IShare::TYPE_USER
1639
+            && $share->getSharedWith() === $this->userId
1640
+        ) {
1641
+            return true;
1642
+        }
1643
+
1644
+        // The owner of the file and the creator of the share
1645
+        // can always delete the share
1646
+        if ($share->getShareOwner() === $this->userId
1647
+            || $share->getSharedBy() === $this->userId
1648
+        ) {
1649
+            return true;
1650
+        }
1651
+
1652
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1653
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1654
+        if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1655
+            return true;
1656
+        }
1657
+
1658
+        return false;
1659
+    }
1660
+
1661
+    /**
1662
+     * Does the user have delete permission on the share
1663
+     * This differs from the canDeleteShare function as it only
1664
+     * remove the share for the current user. It does NOT
1665
+     * completely delete the share but only the mount point.
1666
+     * It can then be restored from the deleted shares section.
1667
+     *
1668
+     * @param IShare $share the share to check
1669
+     * @return boolean
1670
+     *
1671
+     * @suppress PhanUndeclaredClassMethod
1672
+     */
1673
+    protected function canDeleteShareFromSelf(IShare $share): bool {
1674
+        if ($share->getShareType() !== IShare::TYPE_GROUP
1675
+            && $share->getShareType() !== IShare::TYPE_ROOM
1676
+            && $share->getShareType() !== IShare::TYPE_DECK
1677
+            && $share->getShareType() !== IShare::TYPE_SCIENCEMESH
1678
+        ) {
1679
+            return false;
1680
+        }
1681
+
1682
+        if ($share->getShareOwner() === $this->userId
1683
+            || $share->getSharedBy() === $this->userId
1684
+        ) {
1685
+            // Delete the whole share, not just for self
1686
+            return false;
1687
+        }
1688
+
1689
+        // If in the recipient group, you can delete the share from self
1690
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1691
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1692
+            $user = $this->userManager->get($this->userId);
1693
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1694
+                return true;
1695
+            }
1696
+        }
1697
+
1698
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1699
+            try {
1700
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1701
+            } catch (ContainerExceptionInterface $e) {
1702
+                return false;
1703
+            }
1704
+        }
1705
+
1706
+        if ($share->getShareType() === IShare::TYPE_DECK) {
1707
+            try {
1708
+                return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1709
+            } catch (ContainerExceptionInterface $e) {
1710
+                return false;
1711
+            }
1712
+        }
1713
+
1714
+        if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1715
+            try {
1716
+                return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1717
+            } catch (ContainerExceptionInterface $e) {
1718
+                return false;
1719
+            }
1720
+        }
1721
+
1722
+        return false;
1723
+    }
1724
+
1725
+    /**
1726
+     * Make sure that the passed date is valid ISO 8601
1727
+     * So YYYY-MM-DD
1728
+     * If not throw an exception
1729
+     *
1730
+     * @param string $expireDate
1731
+     *
1732
+     * @throws \Exception
1733
+     * @return \DateTime
1734
+     */
1735
+    private function parseDate(string $expireDate): \DateTime {
1736
+        try {
1737
+            $date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
1738
+            // Make sure it expires at midnight in owner timezone
1739
+            $date->setTime(0, 0, 0);
1740
+        } catch (\Exception $e) {
1741
+            throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
1742
+        }
1743
+
1744
+        return $date;
1745
+    }
1746
+
1747
+    /**
1748
+     * Since we have multiple providers but the OCS Share API v1 does
1749
+     * not support this we need to check all backends.
1750
+     *
1751
+     * @param string $id
1752
+     * @return IShare
1753
+     * @throws ShareNotFound
1754
+     */
1755
+    private function getShareById(string $id): IShare {
1756
+        $providers = [
1757
+            'ocinternal' => null, // No type check needed
1758
+            'ocCircleShare' => IShare::TYPE_CIRCLE,
1759
+            'ocMailShare' => IShare::TYPE_EMAIL,
1760
+            'ocRoomShare' => null,
1761
+            'deck' => IShare::TYPE_DECK,
1762
+            'sciencemesh' => IShare::TYPE_SCIENCEMESH,
1763
+        ];
1764
+
1765
+        // Add federated sharing as a provider only if it's allowed
1766
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1767
+            $providers['ocFederatedSharing'] = null; // No type check needed
1768
+        }
1769
+
1770
+        foreach ($providers as $prefix => $type) {
1771
+            try {
1772
+                if ($type === null || $this->shareManager->shareProviderExists($type)) {
1773
+                    return $this->shareManager->getShareById($prefix . ':' . $id, $this->userId);
1774
+                }
1775
+            } catch (ShareNotFound $e) {
1776
+                // Do nothing, continue to next provider
1777
+            } catch (\Exception $e) {
1778
+                $this->logger->warning('Unexpected error in share provider', [
1779
+                    'shareId' => $id,
1780
+                    'provider' => $prefix,
1781
+                    'exception' => $e,
1782
+                ]);
1783
+            }
1784
+        }
1785
+        throw new ShareNotFound();
1786
+    }
1787
+
1788
+    /**
1789
+     * Lock a Node
1790
+     *
1791
+     * @param Node $node
1792
+     * @throws LockedException
1793
+     */
1794
+    private function lock(Node $node) {
1795
+        $node->lock(ILockingProvider::LOCK_SHARED);
1796
+        $this->lockedNode = $node;
1797
+    }
1798
+
1799
+    /**
1800
+     * Cleanup the remaining locks
1801
+     * @throws LockedException
1802
+     */
1803
+    public function cleanup() {
1804
+        if ($this->lockedNode !== null) {
1805
+            $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1806
+        }
1807
+    }
1808
+
1809
+    /**
1810
+     * Returns the helper of ShareAPIController for room shares.
1811
+     *
1812
+     * If the Talk application is not enabled or the helper is not available
1813
+     * a ContainerExceptionInterface is thrown instead.
1814
+     *
1815
+     * @return \OCA\Talk\Share\Helper\ShareAPIController
1816
+     * @throws ContainerExceptionInterface
1817
+     */
1818
+    private function getRoomShareHelper() {
1819
+        if (!$this->appManager->isEnabledForUser('spreed')) {
1820
+            throw new QueryException();
1821
+        }
1822
+
1823
+        return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
1824
+    }
1825
+
1826
+    /**
1827
+     * Returns the helper of ShareAPIHelper for deck shares.
1828
+     *
1829
+     * If the Deck application is not enabled or the helper is not available
1830
+     * a ContainerExceptionInterface is thrown instead.
1831
+     *
1832
+     * @return ShareAPIHelper
1833
+     * @throws ContainerExceptionInterface
1834
+     */
1835
+    private function getDeckShareHelper() {
1836
+        if (!$this->appManager->isEnabledForUser('deck')) {
1837
+            throw new QueryException();
1838
+        }
1839
+
1840
+        return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
1841
+    }
1842
+
1843
+    /**
1844
+     * Returns the helper of ShareAPIHelper for sciencemesh shares.
1845
+     *
1846
+     * If the sciencemesh application is not enabled or the helper is not available
1847
+     * a ContainerExceptionInterface is thrown instead.
1848
+     *
1849
+     * @return ShareAPIHelper
1850
+     * @throws ContainerExceptionInterface
1851
+     */
1852
+    private function getSciencemeshShareHelper() {
1853
+        if (!$this->appManager->isEnabledForUser('sciencemesh')) {
1854
+            throw new QueryException();
1855
+        }
1856
+
1857
+        return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
1858
+    }
1859
+
1860
+    /**
1861
+     * @param string $viewer
1862
+     * @param Node $node
1863
+     * @param bool $reShares
1864
+     *
1865
+     * @return IShare[]
1866
+     */
1867
+    private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1868
+        $providers = [
1869
+            IShare::TYPE_USER,
1870
+            IShare::TYPE_GROUP,
1871
+            IShare::TYPE_LINK,
1872
+            IShare::TYPE_EMAIL,
1873
+            IShare::TYPE_CIRCLE,
1874
+            IShare::TYPE_ROOM,
1875
+            IShare::TYPE_DECK,
1876
+            IShare::TYPE_SCIENCEMESH
1877
+        ];
1878
+
1879
+        // Should we assume that the (currentUser) viewer is the owner of the node !?
1880
+        $shares = [];
1881
+        foreach ($providers as $provider) {
1882
+            if (!$this->shareManager->shareProviderExists($provider)) {
1883
+                continue;
1884
+            }
1885
+
1886
+            $providerShares
1887
+                = $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1888
+            $shares = array_merge($shares, $providerShares);
1889
+        }
1890
+
1891
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1892
+            $federatedShares = $this->shareManager->getSharesBy(
1893
+                $this->userId, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1894
+            );
1895
+            $shares = array_merge($shares, $federatedShares);
1896
+        }
1897
+
1898
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1899
+            $federatedShares = $this->shareManager->getSharesBy(
1900
+                $this->userId, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1901
+            );
1902
+            $shares = array_merge($shares, $federatedShares);
1903
+        }
1904
+
1905
+        return $shares;
1906
+    }
1907
+
1908
+
1909
+    /**
1910
+     * @param Node $node
1911
+     *
1912
+     * @throws SharingRightsException
1913
+     */
1914
+    private function confirmSharingRights(Node $node): void {
1915
+        if (!$this->hasResharingRights($this->userId, $node)) {
1916
+            throw new SharingRightsException($this->l->t('No sharing rights on this item'));
1917
+        }
1918
+    }
1919
+
1920
+
1921
+    /**
1922
+     * @param string $viewer
1923
+     * @param Node $node
1924
+     *
1925
+     * @return bool
1926
+     */
1927
+    private function hasResharingRights($viewer, $node): bool {
1928
+        if ($viewer === $node->getOwner()->getUID()) {
1929
+            return true;
1930
+        }
1931
+
1932
+        foreach ([$node, $node->getParent()] as $node) {
1933
+            $shares = $this->getSharesFromNode($viewer, $node, true);
1934
+            foreach ($shares as $share) {
1935
+                try {
1936
+                    if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1937
+                        return true;
1938
+                    }
1939
+                } catch (InvalidPathException|NotFoundException $e) {
1940
+                }
1941
+            }
1942
+        }
1943
+
1944
+        return false;
1945
+    }
1946
+
1947
+
1948
+    /**
1949
+     * Returns if we can find resharing rights in an IShare object for a specific user.
1950
+     *
1951
+     * @suppress PhanUndeclaredClassMethod
1952
+     *
1953
+     * @param string $userId
1954
+     * @param IShare $share
1955
+     * @param Node $node
1956
+     *
1957
+     * @return bool
1958
+     * @throws NotFoundException
1959
+     * @throws InvalidPathException
1960
+     */
1961
+    private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1962
+        if ($share->getShareOwner() === $userId) {
1963
+            return true;
1964
+        }
1965
+
1966
+        // we check that current user have parent resharing rights on the current file
1967
+        if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1968
+            return true;
1969
+        }
1970
+
1971
+        if ((Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1972
+            return false;
1973
+        }
1974
+
1975
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1976
+            return true;
1977
+        }
1978
+
1979
+        if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1980
+            return true;
1981
+        }
1982
+
1983
+        if ($share->getShareType() === IShare::TYPE_CIRCLE && Server::get(IAppManager::class)->isEnabledForUser('circles')
1984
+            && class_exists('\OCA\Circles\Api\v1\Circles')) {
1985
+            $hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
1986
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1987
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1988
+            if ($shareWithLength === false) {
1989
+                $sharedWith = substr($share->getSharedWith(), $shareWithStart);
1990
+            } else {
1991
+                $sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1992
+            }
1993
+            try {
1994
+                $member = Circles::getMember($sharedWith, $userId, 1);
1995
+                if ($member->getLevel() >= 4) {
1996
+                    return true;
1997
+                }
1998
+                return false;
1999
+            } catch (ContainerExceptionInterface $e) {
2000
+                return false;
2001
+            }
2002
+        }
2003
+
2004
+        return false;
2005
+    }
2006
+
2007
+    /**
2008
+     * Get all the shares for the current user
2009
+     *
2010
+     * @param Node|null $path
2011
+     * @param boolean $reshares
2012
+     * @return IShare[]
2013
+     */
2014
+    private function getAllShares(?Node $path = null, bool $reshares = false) {
2015
+        // Get all shares
2016
+        $userShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_USER, $path, $reshares, -1, 0);
2017
+        $groupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
2018
+        $linkShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_LINK, $path, $reshares, -1, 0);
2019
+
2020
+        // EMAIL SHARES
2021
+        $mailShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
2022
+
2023
+        // TEAM SHARES
2024
+        $circleShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
2025
+
2026
+        // TALK SHARES
2027
+        $roomShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
2028
+
2029
+        // DECK SHARES
2030
+        $deckShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_DECK, $path, $reshares, -1, 0);
2031
+
2032
+        // SCIENCEMESH SHARES
2033
+        $sciencemeshShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_SCIENCEMESH, $path, $reshares, -1, 0);
2034
+
2035
+        // FEDERATION
2036
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
2037
+            $federatedShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
2038
+        } else {
2039
+            $federatedShares = [];
2040
+        }
2041
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
2042
+            $federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
2043
+        } else {
2044
+            $federatedGroupShares = [];
2045
+        }
2046
+
2047
+        return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $federatedShares, $federatedGroupShares);
2048
+    }
2049
+
2050
+
2051
+    /**
2052
+     * merging already formatted shares.
2053
+     * We'll make an associative array to easily detect duplicate Ids.
2054
+     * Keys _needs_ to be removed after all shares are retrieved and merged.
2055
+     *
2056
+     * @param array $shares
2057
+     * @param array $newShares
2058
+     */
2059
+    private function mergeFormattedShares(array &$shares, array $newShares) {
2060
+        foreach ($newShares as $newShare) {
2061
+            if (!array_key_exists($newShare['id'], $shares)) {
2062
+                $shares[$newShare['id']] = $newShare;
2063
+            }
2064
+        }
2065
+    }
2066
+
2067
+    /**
2068
+     * @param IShare $share
2069
+     * @param string|null $attributesString
2070
+     * @return IShare modified share
2071
+     */
2072
+    private function setShareAttributes(IShare $share, ?string $attributesString) {
2073
+        $newShareAttributes = null;
2074
+        if ($attributesString !== null) {
2075
+            $newShareAttributes = $this->shareManager->newShare()->newAttributes();
2076
+            $formattedShareAttributes = \json_decode($attributesString, true);
2077
+            if (is_array($formattedShareAttributes)) {
2078
+                foreach ($formattedShareAttributes as $formattedAttr) {
2079
+                    $newShareAttributes->setAttribute(
2080
+                        $formattedAttr['scope'],
2081
+                        $formattedAttr['key'],
2082
+                        $formattedAttr['value'],
2083
+                    );
2084
+                }
2085
+            } else {
2086
+                throw new OCSBadRequestException($this->l->t('Invalid share attributes provided: "%s"', [$attributesString]));
2087
+            }
2088
+        }
2089
+        $share->setAttributes($newShareAttributes);
2090
+
2091
+        return $share;
2092
+    }
2093
+
2094
+    private function checkInheritedAttributes(IShare $share): void {
2095
+        if (!$share->getSharedBy()) {
2096
+            return; // Probably in a test
2097
+        }
2098
+
2099
+        $canDownload = false;
2100
+        $hideDownload = true;
2101
+        $userExplicitlySetHideDownload = $share->getHideDownload(); // Capture user's explicit choice
2102
+
2103
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
2104
+        $nodes = $userFolder->getById($share->getNodeId());
2105
+        foreach ($nodes as $node) {
2106
+            // Owner always can download it - so allow it, but respect their explicit choice about hiding downloads
2107
+            if ($node->getOwner()?->getUID() === $share->getSharedBy()) {
2108
+                $canDownload = true;
2109
+                $hideDownload = $userExplicitlySetHideDownload;
2110
+                break;
2111
+            }
2112
+
2113
+            if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
2114
+                $storage = $node->getStorage();
2115
+                if ($storage instanceof Wrapper) {
2116
+                    $storage = $storage->getInstanceOfStorage(SharedStorage::class);
2117
+                    if ($storage === null) {
2118
+                        throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
2119
+                    }
2120
+                } else {
2121
+                    throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
2122
+                }
2123
+
2124
+                /** @var SharedStorage $storage */
2125
+                $originalShare = $storage->getShare();
2126
+                $inheritedAttributes = $originalShare->getAttributes();
2127
+
2128
+                // For federated shares: users can only be MORE restrictive, never LESS restrictive
2129
+                // If parent has hideDownload=true, child MUST have hideDownload=true
2130
+                $parentHidesDownload = $originalShare->getHideDownload();
2131
+
2132
+                // Check if download permission is available from parent
2133
+                $parentAllowsDownload = $inheritedAttributes === null || $inheritedAttributes->getAttribute('permissions', 'download') !== false;
2134
+
2135
+                // Apply inheritance rules:
2136
+                // 1. If parent hides download, child must hide download
2137
+                // 2. If parent allows download, child can choose to hide or allow
2138
+                // 3. If parent forbids download, child cannot allow download
2139
+                $hideDownload = $parentHidesDownload || $userExplicitlySetHideDownload;
2140
+
2141
+                $canDownload = $canDownload || $parentAllowsDownload;
2142
+
2143
+            } elseif ($node->getStorage()->instanceOfStorage(Storage::class)) {
2144
+                $canDownload = true; // in case of federation storage, we can expect the download to be activated by default
2145
+                // For external federation storage, respect user's choice if downloads are available
2146
+                $hideDownload = $userExplicitlySetHideDownload;
2147
+            }
2148
+        }
2149
+
2150
+        // Apply the final restrictions:
2151
+        // 1. If parent doesn't allow downloads at all, force hide and disable download attribute
2152
+        // 2. If parent allows downloads, respect user's hideDownload choice
2153
+        if (!$canDownload) {
2154
+            // Parent completely forbids downloads - must enforce this restriction
2155
+            $share->setHideDownload(true);
2156
+            $attributes = $share->getAttributes() ?? $share->newAttributes();
2157
+            $attributes->setAttribute('permissions', 'download', false);
2158
+            $share->setAttributes($attributes);
2159
+        } elseif ($hideDownload) {
2160
+            // Either parent forces hide, or user chooses to hide - respect this
2161
+            $share->setHideDownload(true);
2162
+        } else {
2163
+            // User explicitly wants to allow downloads and parent permits it
2164
+            $share->setHideDownload(false);
2165
+        }
2166
+    }
2167
+
2168
+    /**
2169
+     * Send a mail notification again for a share.
2170
+     * The mail_send option must be enabled for the given share.
2171
+     * @param string $id the share ID
2172
+     * @param string $password the password to check against. Necessary for password protected shares.
2173
+     * @throws OCSNotFoundException Share not found
2174
+     * @throws OCSForbiddenException You are not allowed to send mail notifications
2175
+     * @throws OCSBadRequestException Invalid request or wrong password
2176
+     * @throws OCSException Error while sending mail notification
2177
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
2178
+     *
2179
+     * 200: The email notification was sent successfully
2180
+     */
2181
+    #[NoAdminRequired]
2182
+    #[UserRateLimit(limit: 10, period: 600)]
2183
+    public function sendShareEmail(string $id, $password = ''): DataResponse {
2184
+        try {
2185
+            $share = $this->getShareById($id);
2186
+
2187
+            if (!$this->canAccessShare($share, false)) {
2188
+                throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2189
+            }
2190
+
2191
+            if (!$this->canEditShare($share)) {
2192
+                throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2193
+            }
2194
+
2195
+            // For mail and link shares, the user must be
2196
+            // the owner of the share, not only the file owner.
2197
+            if ($share->getShareType() === IShare::TYPE_EMAIL
2198
+                || $share->getShareType() === IShare::TYPE_LINK) {
2199
+                if ($share->getSharedBy() !== $this->userId) {
2200
+                    throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2201
+                }
2202
+            }
2203
+
2204
+            try {
2205
+                $provider = $this->factory->getProviderForType($share->getShareType());
2206
+                if (!($provider instanceof IShareProviderWithNotification)) {
2207
+                    throw new OCSBadRequestException($this->l->t('No mail notification configured for this share type'));
2208
+                }
2209
+
2210
+                // Circumvent the password encrypted data by
2211
+                // setting the password clear. We're not storing
2212
+                // the password clear, it is just a temporary
2213
+                // object manipulation. The password will stay
2214
+                // encrypted in the database.
2215
+                if ($share->getPassword() !== null && $share->getPassword() !== $password) {
2216
+                    if (!$this->shareManager->checkPassword($share, $password)) {
2217
+                        throw new OCSBadRequestException($this->l->t('Wrong password'));
2218
+                    }
2219
+                    $share = $share->setPassword($password);
2220
+                }
2221
+
2222
+                $provider->sendMailNotification($share);
2223
+                return new DataResponse();
2224
+            } catch (Exception $e) {
2225
+                $this->logger->error($e->getMessage(), ['exception' => $e]);
2226
+                throw new OCSException($this->l->t('Error while sending mail notification'));
2227
+            }
2228
+
2229
+        } catch (ShareNotFound $e) {
2230
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2231
+        }
2232
+    }
2233
+
2234
+    /**
2235
+     * Get a unique share token
2236
+     *
2237
+     * @throws OCSException Failed to generate a unique token
2238
+     *
2239
+     * @return DataResponse<Http::STATUS_OK, array{token: string}, array{}>
2240
+     *
2241
+     * 200: Token generated successfully
2242
+     */
2243
+    #[ApiRoute(verb: 'GET', url: '/api/v1/token')]
2244
+    #[NoAdminRequired]
2245
+    public function generateToken(): DataResponse {
2246
+        try {
2247
+            $token = $this->shareManager->generateToken();
2248
+            return new DataResponse([
2249
+                'token' => $token,
2250
+            ]);
2251
+        } catch (ShareTokenException $e) {
2252
+            throw new OCSException($this->l->t('Failed to generate a unique token'));
2253
+        }
2254
+    }
2255
+
2256
+    /**
2257
+     * Populate the result set with file tags
2258
+     *
2259
+     * @psalm-template T of array{tags?: list<string>, file_source: int, ...array<string, mixed>}
2260
+     * @param list<T> $fileList
2261
+     * @return list<T> file list populated with tags
2262
+     */
2263
+    private function populateTags(array $fileList): array {
2264
+        $tagger = $this->tagManager->load('files');
2265
+        $tags = $tagger->getTagsForObjects(array_map(static fn (array $fileData) => $fileData['file_source'], $fileList));
2266
+
2267
+        if (!is_array($tags)) {
2268
+            throw new \UnexpectedValueException('$tags must be an array');
2269
+        }
2270
+
2271
+        // Set empty tag array
2272
+        foreach ($fileList as &$fileData) {
2273
+            $fileData['tags'] = [];
2274
+        }
2275
+        unset($fileData);
2276
+
2277
+        if (!empty($tags)) {
2278
+            foreach ($tags as $fileId => $fileTags) {
2279
+                foreach ($fileList as &$fileData) {
2280
+                    if ($fileId !== $fileData['file_source']) {
2281
+                        continue;
2282
+                    }
2283
+
2284
+                    $fileData['tags'] = $fileTags;
2285
+                }
2286
+                unset($fileData);
2287
+            }
2288
+        }
2289
+
2290
+        return $fileList;
2291
+    }
2292 2292
 }
Please login to merge, or discard this patch.