Completed
Push — master ( 5d14f8...2337bd )
by Robin
27:19 queued 16s
created
apps/files_sharing/lib/Controller/DeletedShareAPIController.php 1 patch
Indentation   +203 added lines, -203 removed lines patch added patch discarded remove patch
@@ -35,207 +35,207 @@
 block discarded – undo
35 35
  */
36 36
 class DeletedShareAPIController extends OCSController {
37 37
 
38
-	public function __construct(
39
-		string $appName,
40
-		IRequest $request,
41
-		private ShareManager $shareManager,
42
-		private ?string $userId,
43
-		private IUserManager $userManager,
44
-		private IGroupManager $groupManager,
45
-		private IRootFolder $rootFolder,
46
-		private IAppManager $appManager,
47
-		private IServerContainer $serverContainer,
48
-	) {
49
-		parent::__construct($appName, $request);
50
-	}
51
-
52
-	/**
53
-	 * @suppress PhanUndeclaredClassMethod
54
-	 *
55
-	 * @return Files_SharingDeletedShare
56
-	 */
57
-	private function formatShare(IShare $share): array {
58
-		$result = [
59
-			'id' => $share->getFullId(),
60
-			'share_type' => $share->getShareType(),
61
-			'uid_owner' => $share->getSharedBy(),
62
-			'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(),
63
-			'permissions' => 0,
64
-			'stime' => $share->getShareTime()->getTimestamp(),
65
-			'parent' => null,
66
-			'expiration' => null,
67
-			'token' => null,
68
-			'uid_file_owner' => $share->getShareOwner(),
69
-			'displayname_file_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(),
70
-			'path' => $share->getTarget(),
71
-		];
72
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
73
-		$node = $userFolder->getFirstNodeById($share->getNodeId());
74
-		if (!$node) {
75
-			// fallback to guessing the path
76
-			$node = $userFolder->get($share->getTarget());
77
-			if ($node === null || $share->getTarget() === '') {
78
-				throw new NotFoundException();
79
-			}
80
-		}
81
-
82
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
83
-		if ($node instanceof Folder) {
84
-			$result['item_type'] = 'folder';
85
-		} else {
86
-			$result['item_type'] = 'file';
87
-		}
88
-		$result['mimetype'] = $node->getMimetype();
89
-		$result['storage_id'] = $node->getStorage()->getId();
90
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
91
-		$result['item_source'] = $node->getId();
92
-		$result['file_source'] = $node->getId();
93
-		$result['file_parent'] = $node->getParent()->getId();
94
-		$result['file_target'] = $share->getTarget();
95
-		$result['item_size'] = $node->getSize();
96
-		$result['item_mtime'] = $node->getMTime();
97
-
98
-		$expiration = $share->getExpirationDate();
99
-		if ($expiration !== null) {
100
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
101
-		}
102
-
103
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
104
-			$group = $this->groupManager->get($share->getSharedWith());
105
-			$result['share_with'] = $share->getSharedWith();
106
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
107
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
108
-			$result['share_with'] = $share->getSharedWith();
109
-			$result['share_with_displayname'] = '';
110
-
111
-			try {
112
-				$result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
113
-			} catch (QueryException $e) {
114
-			}
115
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
116
-			$result['share_with'] = $share->getSharedWith();
117
-			$result['share_with_displayname'] = '';
118
-
119
-			try {
120
-				$result = array_merge($result, $this->getDeckShareHelper()->formatShare($share));
121
-			} catch (QueryException $e) {
122
-			}
123
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
124
-			$result['share_with'] = $share->getSharedWith();
125
-			$result['share_with_displayname'] = '';
126
-
127
-			try {
128
-				$result = array_merge($result, $this->getSciencemeshShareHelper()->formatShare($share));
129
-			} catch (QueryException $e) {
130
-			}
131
-		}
132
-
133
-		return $result;
134
-	}
135
-
136
-	/**
137
-	 * Get a list of all deleted shares
138
-	 *
139
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingDeletedShare>, array{}>
140
-	 *
141
-	 * 200: Deleted shares returned
142
-	 */
143
-	#[NoAdminRequired]
144
-	public function index(): DataResponse {
145
-		$groupShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_GROUP, null, -1, 0);
146
-		$teamShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_CIRCLE, null, -1, 0);
147
-		$roomShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_ROOM, null, -1, 0);
148
-		$deckShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_DECK, null, -1, 0);
149
-		$sciencemeshShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, null, -1, 0);
150
-
151
-		$shares = array_merge($groupShares, $teamShares, $roomShares, $deckShares, $sciencemeshShares);
152
-
153
-		$shares = array_values(array_map(function (IShare $share) {
154
-			return $this->formatShare($share);
155
-		}, $shares));
156
-
157
-		return new DataResponse($shares);
158
-	}
159
-
160
-	/**
161
-	 * Undelete a deleted share
162
-	 *
163
-	 * @param string $id ID of the share
164
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
165
-	 * @throws OCSException
166
-	 * @throws OCSNotFoundException Share not found
167
-	 *
168
-	 * 200: Share undeleted successfully
169
-	 */
170
-	#[NoAdminRequired]
171
-	public function undelete(string $id): DataResponse {
172
-		try {
173
-			$share = $this->shareManager->getShareById($id, $this->userId);
174
-		} catch (ShareNotFound $e) {
175
-			throw new OCSNotFoundException('Share not found');
176
-		}
177
-
178
-		if ($share->getPermissions() !== 0) {
179
-			throw new OCSNotFoundException('No deleted share found');
180
-		}
181
-
182
-		try {
183
-			$this->shareManager->restoreShare($share, $this->userId);
184
-		} catch (GenericShareException $e) {
185
-			throw new OCSException('Something went wrong');
186
-		}
187
-
188
-		return new DataResponse([]);
189
-	}
190
-
191
-	/**
192
-	 * Returns the helper of DeletedShareAPIController for room shares.
193
-	 *
194
-	 * If the Talk application is not enabled or the helper is not available
195
-	 * a QueryException is thrown instead.
196
-	 *
197
-	 * @return \OCA\Talk\Share\Helper\DeletedShareAPIController
198
-	 * @throws QueryException
199
-	 */
200
-	private function getRoomShareHelper() {
201
-		if (!$this->appManager->isEnabledForUser('spreed')) {
202
-			throw new QueryException();
203
-		}
204
-
205
-		return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
206
-	}
207
-
208
-	/**
209
-	 * Returns the helper of DeletedShareAPIHelper for deck shares.
210
-	 *
211
-	 * If the Deck application is not enabled or the helper is not available
212
-	 * a QueryException is thrown instead.
213
-	 *
214
-	 * @return ShareAPIHelper
215
-	 * @throws QueryException
216
-	 */
217
-	private function getDeckShareHelper() {
218
-		if (!$this->appManager->isEnabledForUser('deck')) {
219
-			throw new QueryException();
220
-		}
221
-
222
-		return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
223
-	}
224
-
225
-	/**
226
-	 * Returns the helper of DeletedShareAPIHelper for sciencemesh shares.
227
-	 *
228
-	 * If the sciencemesh application is not enabled or the helper is not available
229
-	 * a QueryException is thrown instead.
230
-	 *
231
-	 * @return ShareAPIHelper
232
-	 * @throws QueryException
233
-	 */
234
-	private function getSciencemeshShareHelper() {
235
-		if (!$this->appManager->isEnabledForUser('sciencemesh')) {
236
-			throw new QueryException();
237
-		}
238
-
239
-		return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
240
-	}
38
+    public function __construct(
39
+        string $appName,
40
+        IRequest $request,
41
+        private ShareManager $shareManager,
42
+        private ?string $userId,
43
+        private IUserManager $userManager,
44
+        private IGroupManager $groupManager,
45
+        private IRootFolder $rootFolder,
46
+        private IAppManager $appManager,
47
+        private IServerContainer $serverContainer,
48
+    ) {
49
+        parent::__construct($appName, $request);
50
+    }
51
+
52
+    /**
53
+     * @suppress PhanUndeclaredClassMethod
54
+     *
55
+     * @return Files_SharingDeletedShare
56
+     */
57
+    private function formatShare(IShare $share): array {
58
+        $result = [
59
+            'id' => $share->getFullId(),
60
+            'share_type' => $share->getShareType(),
61
+            'uid_owner' => $share->getSharedBy(),
62
+            'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(),
63
+            'permissions' => 0,
64
+            'stime' => $share->getShareTime()->getTimestamp(),
65
+            'parent' => null,
66
+            'expiration' => null,
67
+            'token' => null,
68
+            'uid_file_owner' => $share->getShareOwner(),
69
+            'displayname_file_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(),
70
+            'path' => $share->getTarget(),
71
+        ];
72
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
73
+        $node = $userFolder->getFirstNodeById($share->getNodeId());
74
+        if (!$node) {
75
+            // fallback to guessing the path
76
+            $node = $userFolder->get($share->getTarget());
77
+            if ($node === null || $share->getTarget() === '') {
78
+                throw new NotFoundException();
79
+            }
80
+        }
81
+
82
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
83
+        if ($node instanceof Folder) {
84
+            $result['item_type'] = 'folder';
85
+        } else {
86
+            $result['item_type'] = 'file';
87
+        }
88
+        $result['mimetype'] = $node->getMimetype();
89
+        $result['storage_id'] = $node->getStorage()->getId();
90
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
91
+        $result['item_source'] = $node->getId();
92
+        $result['file_source'] = $node->getId();
93
+        $result['file_parent'] = $node->getParent()->getId();
94
+        $result['file_target'] = $share->getTarget();
95
+        $result['item_size'] = $node->getSize();
96
+        $result['item_mtime'] = $node->getMTime();
97
+
98
+        $expiration = $share->getExpirationDate();
99
+        if ($expiration !== null) {
100
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
101
+        }
102
+
103
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
104
+            $group = $this->groupManager->get($share->getSharedWith());
105
+            $result['share_with'] = $share->getSharedWith();
106
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
107
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
108
+            $result['share_with'] = $share->getSharedWith();
109
+            $result['share_with_displayname'] = '';
110
+
111
+            try {
112
+                $result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
113
+            } catch (QueryException $e) {
114
+            }
115
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
116
+            $result['share_with'] = $share->getSharedWith();
117
+            $result['share_with_displayname'] = '';
118
+
119
+            try {
120
+                $result = array_merge($result, $this->getDeckShareHelper()->formatShare($share));
121
+            } catch (QueryException $e) {
122
+            }
123
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
124
+            $result['share_with'] = $share->getSharedWith();
125
+            $result['share_with_displayname'] = '';
126
+
127
+            try {
128
+                $result = array_merge($result, $this->getSciencemeshShareHelper()->formatShare($share));
129
+            } catch (QueryException $e) {
130
+            }
131
+        }
132
+
133
+        return $result;
134
+    }
135
+
136
+    /**
137
+     * Get a list of all deleted shares
138
+     *
139
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingDeletedShare>, array{}>
140
+     *
141
+     * 200: Deleted shares returned
142
+     */
143
+    #[NoAdminRequired]
144
+    public function index(): DataResponse {
145
+        $groupShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_GROUP, null, -1, 0);
146
+        $teamShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_CIRCLE, null, -1, 0);
147
+        $roomShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_ROOM, null, -1, 0);
148
+        $deckShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_DECK, null, -1, 0);
149
+        $sciencemeshShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, null, -1, 0);
150
+
151
+        $shares = array_merge($groupShares, $teamShares, $roomShares, $deckShares, $sciencemeshShares);
152
+
153
+        $shares = array_values(array_map(function (IShare $share) {
154
+            return $this->formatShare($share);
155
+        }, $shares));
156
+
157
+        return new DataResponse($shares);
158
+    }
159
+
160
+    /**
161
+     * Undelete a deleted share
162
+     *
163
+     * @param string $id ID of the share
164
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
165
+     * @throws OCSException
166
+     * @throws OCSNotFoundException Share not found
167
+     *
168
+     * 200: Share undeleted successfully
169
+     */
170
+    #[NoAdminRequired]
171
+    public function undelete(string $id): DataResponse {
172
+        try {
173
+            $share = $this->shareManager->getShareById($id, $this->userId);
174
+        } catch (ShareNotFound $e) {
175
+            throw new OCSNotFoundException('Share not found');
176
+        }
177
+
178
+        if ($share->getPermissions() !== 0) {
179
+            throw new OCSNotFoundException('No deleted share found');
180
+        }
181
+
182
+        try {
183
+            $this->shareManager->restoreShare($share, $this->userId);
184
+        } catch (GenericShareException $e) {
185
+            throw new OCSException('Something went wrong');
186
+        }
187
+
188
+        return new DataResponse([]);
189
+    }
190
+
191
+    /**
192
+     * Returns the helper of DeletedShareAPIController for room shares.
193
+     *
194
+     * If the Talk application is not enabled or the helper is not available
195
+     * a QueryException is thrown instead.
196
+     *
197
+     * @return \OCA\Talk\Share\Helper\DeletedShareAPIController
198
+     * @throws QueryException
199
+     */
200
+    private function getRoomShareHelper() {
201
+        if (!$this->appManager->isEnabledForUser('spreed')) {
202
+            throw new QueryException();
203
+        }
204
+
205
+        return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
206
+    }
207
+
208
+    /**
209
+     * Returns the helper of DeletedShareAPIHelper for deck shares.
210
+     *
211
+     * If the Deck application is not enabled or the helper is not available
212
+     * a QueryException is thrown instead.
213
+     *
214
+     * @return ShareAPIHelper
215
+     * @throws QueryException
216
+     */
217
+    private function getDeckShareHelper() {
218
+        if (!$this->appManager->isEnabledForUser('deck')) {
219
+            throw new QueryException();
220
+        }
221
+
222
+        return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
223
+    }
224
+
225
+    /**
226
+     * Returns the helper of DeletedShareAPIHelper for sciencemesh shares.
227
+     *
228
+     * If the sciencemesh application is not enabled or the helper is not available
229
+     * a QueryException is thrown instead.
230
+     *
231
+     * @return ShareAPIHelper
232
+     * @throws QueryException
233
+     */
234
+    private function getSciencemeshShareHelper() {
235
+        if (!$this->appManager->isEnabledForUser('sciencemesh')) {
236
+            throw new QueryException();
237
+        }
238
+
239
+        return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
240
+    }
241 241
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareAPIController.php 1 patch
Indentation   +2184 added lines, -2184 removed lines patch added patch discarded remove patch
@@ -73,2191 +73,2191 @@
 block discarded – undo
73 73
  */
74 74
 class ShareAPIController extends OCSController {
75 75
 
76
-	private ?Node $lockedNode = null;
77
-
78
-	/**
79
-	 * Share20OCS constructor.
80
-	 */
81
-	public function __construct(
82
-		string $appName,
83
-		IRequest $request,
84
-		private IManager $shareManager,
85
-		private IGroupManager $groupManager,
86
-		private IUserManager $userManager,
87
-		private IRootFolder $rootFolder,
88
-		private IURLGenerator $urlGenerator,
89
-		private IL10N $l,
90
-		private IConfig $config,
91
-		private IAppManager $appManager,
92
-		private ContainerInterface $serverContainer,
93
-		private IUserStatusManager $userStatusManager,
94
-		private IPreview $previewManager,
95
-		private IDateTimeZone $dateTimeZone,
96
-		private LoggerInterface $logger,
97
-		private IProviderFactory $factory,
98
-		private IMailer $mailer,
99
-		private ITagManager $tagManager,
100
-		private ?string $userId = null,
101
-	) {
102
-		parent::__construct($appName, $request);
103
-	}
104
-
105
-	/**
106
-	 * Convert an IShare to an array for OCS output
107
-	 *
108
-	 * @param IShare $share
109
-	 * @param Node|null $recipientNode
110
-	 * @return Files_SharingShare
111
-	 * @throws NotFoundException In case the node can't be resolved.
112
-	 *
113
-	 * @suppress PhanUndeclaredClassMethod
114
-	 */
115
-	protected function formatShare(IShare $share, ?Node $recipientNode = null): array {
116
-		$sharedBy = $this->userManager->get($share->getSharedBy());
117
-		$shareOwner = $this->userManager->get($share->getShareOwner());
118
-
119
-		$isOwnShare = false;
120
-		if ($shareOwner !== null) {
121
-			$isOwnShare = $shareOwner->getUID() === $this->userId;
122
-		}
123
-
124
-		$result = [
125
-			'id' => $share->getId(),
126
-			'share_type' => $share->getShareType(),
127
-			'uid_owner' => $share->getSharedBy(),
128
-			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
129
-			// recipient permissions
130
-			'permissions' => $share->getPermissions(),
131
-			// current user permissions on this share
132
-			'can_edit' => $this->canEditShare($share),
133
-			'can_delete' => $this->canDeleteShare($share),
134
-			'stime' => $share->getShareTime()->getTimestamp(),
135
-			'parent' => null,
136
-			'expiration' => null,
137
-			'token' => null,
138
-			'uid_file_owner' => $share->getShareOwner(),
139
-			'note' => $share->getNote(),
140
-			'label' => $share->getLabel(),
141
-			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
142
-		];
143
-
144
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
145
-		if ($recipientNode) {
146
-			$node = $recipientNode;
147
-		} else {
148
-			$node = $userFolder->getFirstNodeById($share->getNodeId());
149
-			if (!$node) {
150
-				// fallback to guessing the path
151
-				$node = $userFolder->get($share->getTarget());
152
-				if ($node === null || $share->getTarget() === '') {
153
-					throw new NotFoundException();
154
-				}
155
-			}
156
-		}
157
-
158
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
159
-		if ($node instanceof Folder) {
160
-			$result['item_type'] = 'folder';
161
-		} else {
162
-			$result['item_type'] = 'file';
163
-		}
164
-
165
-		// Get the original node permission if the share owner is the current user
166
-		if ($isOwnShare) {
167
-			$result['item_permissions'] = $node->getPermissions();
168
-		}
169
-
170
-		// If we're on the recipient side, the node permissions
171
-		// are bound to the share permissions. So we need to
172
-		// adjust the permissions to the share permissions if necessary.
173
-		if (!$isOwnShare) {
174
-			$result['item_permissions'] = $share->getPermissions();
175
-
176
-			// For some reason, single files share are forbidden to have the delete permission
177
-			// since we have custom methods to check those, let's adjust straight away.
178
-			// DAV permissions does not have that issue though.
179
-			if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
180
-				$result['item_permissions'] |= Constants::PERMISSION_DELETE;
181
-			}
182
-			if ($this->canEditShare($share)) {
183
-				$result['item_permissions'] |= Constants::PERMISSION_UPDATE;
184
-			}
185
-		}
186
-
187
-		// See MOUNT_ROOT_PROPERTYNAME dav property
188
-		$result['is-mount-root'] = $node->getInternalPath() === '';
189
-		$result['mount-type'] = $node->getMountPoint()->getMountType();
190
-
191
-		$result['mimetype'] = $node->getMimetype();
192
-		$result['has_preview'] = $this->previewManager->isAvailable($node);
193
-		$result['storage_id'] = $node->getStorage()->getId();
194
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
195
-		$result['item_source'] = $node->getId();
196
-		$result['file_source'] = $node->getId();
197
-		$result['file_parent'] = $node->getParent()->getId();
198
-		$result['file_target'] = $share->getTarget();
199
-		$result['item_size'] = $node->getSize();
200
-		$result['item_mtime'] = $node->getMTime();
201
-
202
-		$expiration = $share->getExpirationDate();
203
-		if ($expiration !== null) {
204
-			$expiration->setTimezone($this->dateTimeZone->getTimeZone());
205
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
206
-		}
207
-
208
-		if ($share->getShareType() === IShare::TYPE_USER) {
209
-			$sharedWith = $this->userManager->get($share->getSharedWith());
210
-			$result['share_with'] = $share->getSharedWith();
211
-			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
212
-			$result['share_with_displayname_unique'] = $sharedWith !== null ? (
213
-				!empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
214
-			) : $share->getSharedWith();
215
-
216
-			$userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
217
-			$userStatus = array_shift($userStatuses);
218
-			if ($userStatus) {
219
-				$result['status'] = [
220
-					'status' => $userStatus->getStatus(),
221
-					'message' => $userStatus->getMessage(),
222
-					'icon' => $userStatus->getIcon(),
223
-					'clearAt' => $userStatus->getClearAt()
224
-						? (int)$userStatus->getClearAt()->format('U')
225
-						: null,
226
-				];
227
-			}
228
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
229
-			$group = $this->groupManager->get($share->getSharedWith());
230
-			$result['share_with'] = $share->getSharedWith();
231
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
232
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
233
-
234
-			// "share_with" and "share_with_displayname" for passwords of link
235
-			// shares was deprecated in Nextcloud 15, use "password" instead.
236
-			$result['share_with'] = $share->getPassword();
237
-			$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
238
-
239
-			$result['password'] = $share->getPassword();
240
-
241
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
242
-
243
-			$result['token'] = $share->getToken();
244
-			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
245
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
246
-			$result['share_with'] = $share->getSharedWith();
247
-			$result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
248
-			$result['token'] = $share->getToken();
249
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
250
-			$result['share_with'] = $share->getSharedWith();
251
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
252
-			$result['token'] = $share->getToken();
253
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
254
-			$result['share_with'] = $share->getSharedWith();
255
-			$result['password'] = $share->getPassword();
256
-			$result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
257
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
258
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
259
-			$result['token'] = $share->getToken();
260
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
261
-			// getSharedWith() returns either "name (type, owner)" or
262
-			// "name (type, owner) [id]", depending on the Teams app version.
263
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
264
-
265
-			$result['share_with_displayname'] = $share->getSharedWithDisplayName();
266
-			if (empty($result['share_with_displayname'])) {
267
-				$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
268
-				$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
269
-			}
270
-
271
-			$result['share_with_avatar'] = $share->getSharedWithAvatar();
272
-
273
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
274
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
275
-			if ($shareWithLength === false) {
276
-				$result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
277
-			} else {
278
-				$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
279
-			}
280
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
281
-			$result['share_with'] = $share->getSharedWith();
282
-			$result['share_with_displayname'] = '';
283
-
284
-			try {
285
-				/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
286
-				$roomShare = $this->getRoomShareHelper()->formatShare($share);
287
-				$result = array_merge($result, $roomShare);
288
-			} catch (ContainerExceptionInterface $e) {
289
-			}
290
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
291
-			$result['share_with'] = $share->getSharedWith();
292
-			$result['share_with_displayname'] = '';
293
-
294
-			try {
295
-				/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
296
-				$deckShare = $this->getDeckShareHelper()->formatShare($share);
297
-				$result = array_merge($result, $deckShare);
298
-			} catch (ContainerExceptionInterface $e) {
299
-			}
300
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
301
-			$result['share_with'] = $share->getSharedWith();
302
-			$result['share_with_displayname'] = '';
303
-
304
-			try {
305
-				/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
306
-				$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
307
-				$result = array_merge($result, $scienceMeshShare);
308
-			} catch (ContainerExceptionInterface $e) {
309
-			}
310
-		}
311
-
312
-
313
-		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
314
-		$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
315
-
316
-		$result['attributes'] = null;
317
-		if ($attributes = $share->getAttributes()) {
318
-			$result['attributes'] = (string)\json_encode($attributes->toArray());
319
-		}
320
-
321
-		return $result;
322
-	}
323
-
324
-	/**
325
-	 * Check if one of the users address books knows the exact property, if
326
-	 * not we return the full name.
327
-	 *
328
-	 * @param string $query
329
-	 * @param string $property
330
-	 * @return string
331
-	 */
332
-	private function getDisplayNameFromAddressBook(string $query, string $property): string {
333
-		// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
334
-		try {
335
-			$result = Server::get(\OCP\Contacts\IManager::class)->search($query, [$property], [
336
-				'limit' => 1,
337
-				'enumeration' => false,
338
-				'strict_search' => true,
339
-			]);
340
-		} catch (Exception $e) {
341
-			$this->logger->error(
342
-				$e->getMessage(),
343
-				['exception' => $e]
344
-			);
345
-			return $query;
346
-		}
347
-
348
-		foreach ($result as $r) {
349
-			foreach ($r[$property] as $value) {
350
-				if ($value === $query && $r['FN']) {
351
-					return $r['FN'];
352
-				}
353
-			}
354
-		}
355
-
356
-		return $query;
357
-	}
358
-
359
-
360
-	/**
361
-	 * @param list<Files_SharingShare> $shares
362
-	 * @param array<string, string>|null $updatedDisplayName
363
-	 *
364
-	 * @return list<Files_SharingShare>
365
-	 */
366
-	private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
367
-		$userIds = $updated = [];
368
-		foreach ($shares as $share) {
369
-			// share is federated and share have no display name yet
370
-			if ($share['share_type'] === IShare::TYPE_REMOTE
371
-				&& ($share['share_with'] ?? '') !== ''
372
-				&& ($share['share_with_displayname'] ?? '') === '') {
373
-				$userIds[] = $userId = $share['share_with'];
374
-
375
-				if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
376
-					$share['share_with_displayname'] = $updatedDisplayName[$userId];
377
-				}
378
-			}
379
-
380
-			// prepping userIds with displayName to be updated
381
-			$updated[] = $share;
382
-		}
383
-
384
-		// if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
385
-		if ($updatedDisplayName !== null) {
386
-			return $updated;
387
-		}
388
-
389
-		// get displayName for the generated list of userId with no displayName
390
-		$displayNames = $this->retrieveFederatedDisplayName($userIds);
391
-
392
-		// if no displayName are updated, we exit
393
-		if (empty($displayNames)) {
394
-			return $updated;
395
-		}
396
-
397
-		// let's fix missing display name and returns all shares
398
-		return $this->fixMissingDisplayName($shares, $displayNames);
399
-	}
400
-
401
-
402
-	/**
403
-	 * get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
404
-	 * returns an array with userIds as keys and displayName as values.
405
-	 *
406
-	 * @param array $userIds
407
-	 * @param bool $cacheOnly - do not reach LUS, get data from cache.
408
-	 *
409
-	 * @return array
410
-	 * @throws ContainerExceptionInterface
411
-	 */
412
-	private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
413
-		// check if gss is enabled and available
414
-		if (count($userIds) === 0
415
-			|| !$this->appManager->isEnabledForAnyone('globalsiteselector')
416
-			|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
417
-			return [];
418
-		}
419
-
420
-		try {
421
-			$slaveService = Server::get(SlaveService::class);
422
-		} catch (\Throwable $e) {
423
-			$this->logger->error(
424
-				$e->getMessage(),
425
-				['exception' => $e]
426
-			);
427
-			return [];
428
-		}
429
-
430
-		return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
431
-	}
432
-
433
-
434
-	/**
435
-	 * retrieve displayName from cache if available (should be used on federated shares)
436
-	 * if not available in cache/lus, try for get from address-book, else returns empty string.
437
-	 *
438
-	 * @param string $userId
439
-	 * @param bool $cacheOnly if true will not reach the lus but will only get data from cache
440
-	 *
441
-	 * @return string
442
-	 */
443
-	private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
444
-		$details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
445
-		if (array_key_exists($userId, $details)) {
446
-			return $details[$userId];
447
-		}
448
-
449
-		$displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
450
-		return ($displayName === $userId) ? '' : $displayName;
451
-	}
452
-
453
-
454
-
455
-	/**
456
-	 * Get a specific share by id
457
-	 *
458
-	 * @param string $id ID of the share
459
-	 * @param bool $include_tags Include tags in the share
460
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
461
-	 * @throws OCSNotFoundException Share not found
462
-	 *
463
-	 * 200: Share returned
464
-	 */
465
-	#[NoAdminRequired]
466
-	public function getShare(string $id, bool $include_tags = false): DataResponse {
467
-		try {
468
-			$share = $this->getShareById($id);
469
-		} catch (ShareNotFound $e) {
470
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
471
-		}
472
-
473
-		try {
474
-			if ($this->canAccessShare($share)) {
475
-				$share = $this->formatShare($share);
476
-
477
-				if ($include_tags) {
478
-					$share = $this->populateTags([$share]);
479
-				} else {
480
-					$share = [$share];
481
-				}
482
-
483
-				return new DataResponse($share);
484
-			}
485
-		} catch (NotFoundException $e) {
486
-			// Fall through
487
-		}
488
-
489
-		throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
490
-	}
491
-
492
-	/**
493
-	 * Delete a share
494
-	 *
495
-	 * @param string $id ID of the share
496
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
497
-	 * @throws OCSNotFoundException Share not found
498
-	 * @throws OCSForbiddenException Missing permissions to delete the share
499
-	 *
500
-	 * 200: Share deleted successfully
501
-	 */
502
-	#[NoAdminRequired]
503
-	public function deleteShare(string $id): DataResponse {
504
-		try {
505
-			$share = $this->getShareById($id);
506
-		} catch (ShareNotFound $e) {
507
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
508
-		}
509
-
510
-		try {
511
-			$this->lock($share->getNode());
512
-		} catch (LockedException $e) {
513
-			throw new OCSNotFoundException($this->l->t('Could not delete share'));
514
-		}
515
-
516
-		if (!$this->canAccessShare($share)) {
517
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
518
-		}
519
-
520
-		// if it's a group share or a room share
521
-		// we don't delete the share, but only the
522
-		// mount point. Allowing it to be restored
523
-		// from the deleted shares
524
-		if ($this->canDeleteShareFromSelf($share)) {
525
-			$this->shareManager->deleteFromSelf($share, $this->userId);
526
-		} else {
527
-			if (!$this->canDeleteShare($share)) {
528
-				throw new OCSForbiddenException($this->l->t('Could not delete share'));
529
-			}
530
-
531
-			$this->shareManager->deleteShare($share);
532
-		}
533
-
534
-		return new DataResponse();
535
-	}
536
-
537
-	/**
538
-	 * Create a share
539
-	 *
540
-	 * @param string|null $path Path of the share
541
-	 * @param int|null $permissions Permissions for the share
542
-	 * @param int $shareType Type of the share
543
-	 * @param ?string $shareWith The entity this should be shared with
544
-	 * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
545
-	 * @param string $password Password for the share
546
-	 * @param string|null $sendPasswordByTalk Send the password for the share over Talk
547
-	 * @param ?string $expireDate The expiry date of the share in the user's timezone at 00:00.
548
-	 *                            If $expireDate is not supplied or set to `null`, the system default will be used.
549
-	 * @param string $note Note for the share
550
-	 * @param string $label Label for the share (only used in link and email)
551
-	 * @param string|null $attributes Additional attributes for the share
552
-	 * @param 'false'|'true'|null $sendMail Send a mail to the recipient
553
-	 *
554
-	 * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
555
-	 * @throws OCSBadRequestException Unknown share type
556
-	 * @throws OCSException
557
-	 * @throws OCSForbiddenException Creating the share is not allowed
558
-	 * @throws OCSNotFoundException Creating the share failed
559
-	 * @suppress PhanUndeclaredClassMethod
560
-	 *
561
-	 * 200: Share created
562
-	 */
563
-	#[NoAdminRequired]
564
-	#[UserRateLimit(limit: 20, period: 600)]
565
-	public function createShare(
566
-		?string $path = null,
567
-		?int $permissions = null,
568
-		int $shareType = -1,
569
-		?string $shareWith = null,
570
-		?string $publicUpload = null,
571
-		string $password = '',
572
-		?string $sendPasswordByTalk = null,
573
-		?string $expireDate = null,
574
-		string $note = '',
575
-		string $label = '',
576
-		?string $attributes = null,
577
-		?string $sendMail = null,
578
-	): DataResponse {
579
-		assert($this->userId !== null);
580
-
581
-		$share = $this->shareManager->newShare();
582
-		$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
583
-
584
-		// Verify path
585
-		if ($path === null) {
586
-			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
587
-		}
588
-
589
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
590
-		try {
591
-			/** @var \OC\Files\Node\Node $node */
592
-			$node = $userFolder->get($path);
593
-		} catch (NotFoundException $e) {
594
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
595
-		}
596
-
597
-		// a user can have access to a file through different paths, with differing permissions
598
-		// combine all permissions to determine if the user can share this file
599
-		$nodes = $userFolder->getById($node->getId());
600
-		foreach ($nodes as $nodeById) {
601
-			/** @var FileInfo $fileInfo */
602
-			$fileInfo = $node->getFileInfo();
603
-			$fileInfo['permissions'] |= $nodeById->getPermissions();
604
-		}
605
-
606
-		$share->setNode($node);
607
-
608
-		try {
609
-			$this->lock($share->getNode());
610
-		} catch (LockedException $e) {
611
-			throw new OCSNotFoundException($this->l->t('Could not create share'));
612
-		}
613
-
614
-		// Set permissions
615
-		if ($shareType === IShare::TYPE_LINK || $shareType === IShare::TYPE_EMAIL) {
616
-			$permissions = $this->getLinkSharePermissions($permissions, $hasPublicUpload);
617
-			$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
618
-		} else {
619
-			// Use default permissions only for non-link shares to keep legacy behavior
620
-			if ($permissions === null) {
621
-				$permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
622
-			}
623
-			// Non-link shares always require read permissions (link shares could be file drop)
624
-			$permissions |= Constants::PERMISSION_READ;
625
-		}
626
-
627
-		// For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
628
-		if ($node instanceof File) {
629
-			// if this is a single file share we remove the DELETE and CREATE permissions
630
-			$permissions = $permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE);
631
-		}
632
-
633
-		/**
634
-		 * Hack for https://github.com/owncloud/core/issues/22587
635
-		 * We check the permissions via webdav. But the permissions of the mount point
636
-		 * do not equal the share permissions. Here we fix that for federated mounts.
637
-		 */
638
-		if ($node->getStorage()->instanceOfStorage(Storage::class)) {
639
-			$permissions &= ~($permissions & ~$node->getPermissions());
640
-		}
641
-
642
-		if ($attributes !== null) {
643
-			$share = $this->setShareAttributes($share, $attributes);
644
-		}
645
-
646
-		// Expire date checks
647
-		// Normally, null means no expiration date but we still set the default for backwards compatibility
648
-		// If the client sends an empty string, we set noExpirationDate to true
649
-		if ($expireDate !== null) {
650
-			if ($expireDate !== '') {
651
-				try {
652
-					$expireDateTime = $this->parseDate($expireDate);
653
-					$share->setExpirationDate($expireDateTime);
654
-				} catch (\Exception $e) {
655
-					throw new OCSNotFoundException($e->getMessage(), $e);
656
-				}
657
-			} else {
658
-				// Client sent empty string for expire date.
659
-				// Set noExpirationDate to true so overwrite is prevented.
660
-				$share->setNoExpirationDate(true);
661
-			}
662
-		}
663
-
664
-		$share->setSharedBy($this->userId);
665
-
666
-		// Handle mail send
667
-		if (is_null($sendMail)) {
668
-			$allowSendMail = $this->config->getSystemValueBool('sharing.enable_share_mail', true);
669
-			if ($allowSendMail !== true || $shareType === IShare::TYPE_EMAIL) {
670
-				// Define a default behavior when sendMail is not provided
671
-				// For email shares with a valid recipient, the default is to send the mail
672
-				// For all other share types, the default is to not send the mail
673
-				$allowSendMail = ($shareType === IShare::TYPE_EMAIL && $shareWith !== null && $shareWith !== '');
674
-			}
675
-			$share->setMailSend($allowSendMail);
676
-		} else {
677
-			$share->setMailSend($sendMail === 'true');
678
-		}
679
-
680
-		if ($shareType === IShare::TYPE_USER) {
681
-			// Valid user is required to share
682
-			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
683
-				throw new OCSNotFoundException($this->l->t('Please specify a valid account to share with'));
684
-			}
685
-			$share->setSharedWith($shareWith);
686
-			$share->setPermissions($permissions);
687
-		} elseif ($shareType === IShare::TYPE_GROUP) {
688
-			if (!$this->shareManager->allowGroupSharing()) {
689
-				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
690
-			}
691
-
692
-			// Valid group is required to share
693
-			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
694
-				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
695
-			}
696
-			$share->setSharedWith($shareWith);
697
-			$share->setPermissions($permissions);
698
-		} elseif ($shareType === IShare::TYPE_LINK
699
-			|| $shareType === IShare::TYPE_EMAIL) {
700
-
701
-			// Can we even share links?
702
-			if (!$this->shareManager->shareApiAllowLinks()) {
703
-				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
704
-			}
705
-
706
-			$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
707
-			$share->setPermissions($permissions);
708
-
709
-			// Set password
710
-			if ($password !== '') {
711
-				$share->setPassword($password);
712
-			}
713
-
714
-			// Only share by mail have a recipient
715
-			if (is_string($shareWith) && $shareType === IShare::TYPE_EMAIL) {
716
-				// If sending a mail have been requested, validate the mail address
717
-				if ($share->getMailSend() && !$this->mailer->validateMailAddress($shareWith)) {
718
-					throw new OCSNotFoundException($this->l->t('Please specify a valid email address'));
719
-				}
720
-				$share->setSharedWith($shareWith);
721
-			}
722
-
723
-			// If we have a label, use it
724
-			if ($label !== '') {
725
-				if (strlen($label) > 255) {
726
-					throw new OCSBadRequestException('Maximum label length is 255');
727
-				}
728
-				$share->setLabel($label);
729
-			}
730
-
731
-			if ($sendPasswordByTalk === 'true') {
732
-				if (!$this->appManager->isEnabledForUser('spreed')) {
733
-					throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$node->getPath()]));
734
-				}
735
-
736
-				$share->setSendPasswordByTalk(true);
737
-			}
738
-		} elseif ($shareType === IShare::TYPE_REMOTE) {
739
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
740
-				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]));
741
-			}
742
-
743
-			if ($shareWith === null) {
744
-				throw new OCSNotFoundException($this->l->t('Please specify a valid federated account ID'));
745
-			}
746
-
747
-			$share->setSharedWith($shareWith);
748
-			$share->setPermissions($permissions);
749
-			$share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
750
-		} elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
751
-			if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
752
-				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]));
753
-			}
754
-
755
-			if ($shareWith === null) {
756
-				throw new OCSNotFoundException($this->l->t('Please specify a valid federated group ID'));
757
-			}
758
-
759
-			$share->setSharedWith($shareWith);
760
-			$share->setPermissions($permissions);
761
-		} elseif ($shareType === IShare::TYPE_CIRCLE) {
762
-			if (!Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
763
-				throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
764
-			}
765
-
766
-			$circle = Circles::detailsCircle($shareWith);
767
-
768
-			// Valid team is required to share
769
-			if ($circle === null) {
770
-				throw new OCSNotFoundException($this->l->t('Please specify a valid team'));
771
-			}
772
-			$share->setSharedWith($shareWith);
773
-			$share->setPermissions($permissions);
774
-		} elseif ($shareType === IShare::TYPE_ROOM) {
775
-			try {
776
-				$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
777
-			} catch (ContainerExceptionInterface $e) {
778
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
779
-			}
780
-		} elseif ($shareType === IShare::TYPE_DECK) {
781
-			try {
782
-				$this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
783
-			} catch (ContainerExceptionInterface $e) {
784
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
785
-			}
786
-		} elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
787
-			try {
788
-				$this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
789
-			} catch (ContainerExceptionInterface $e) {
790
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
791
-			}
792
-		} else {
793
-			throw new OCSBadRequestException($this->l->t('Unknown share type'));
794
-		}
795
-
796
-		$share->setShareType($shareType);
797
-		$this->checkInheritedAttributes($share);
798
-
799
-		if ($note !== '') {
800
-			$share->setNote($note);
801
-		}
802
-
803
-		try {
804
-			$share = $this->shareManager->createShare($share);
805
-		} catch (HintException $e) {
806
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
807
-			throw new OCSException($e->getHint(), $code);
808
-		} catch (GenericShareException|\InvalidArgumentException $e) {
809
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
810
-			throw new OCSForbiddenException($e->getMessage(), $e);
811
-		} catch (\Exception $e) {
812
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
813
-			throw new OCSForbiddenException('Failed to create share.', $e);
814
-		}
815
-
816
-		$output = $this->formatShare($share);
817
-
818
-		return new DataResponse($output);
819
-	}
820
-
821
-	/**
822
-	 * @param null|Node $node
823
-	 * @param boolean $includeTags
824
-	 *
825
-	 * @return list<Files_SharingShare>
826
-	 */
827
-	private function getSharedWithMe($node, bool $includeTags): array {
828
-		$userShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_USER, $node, -1, 0);
829
-		$groupShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_GROUP, $node, -1, 0);
830
-		$circleShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_CIRCLE, $node, -1, 0);
831
-		$roomShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_ROOM, $node, -1, 0);
832
-		$deckShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_DECK, $node, -1, 0);
833
-		$sciencemeshShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, $node, -1, 0);
834
-
835
-		$shares = array_merge($userShares, $groupShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares);
836
-
837
-		$filteredShares = array_filter($shares, function (IShare $share) {
838
-			return $share->getShareOwner() !== $this->userId;
839
-		});
840
-
841
-		$formatted = [];
842
-		foreach ($filteredShares as $share) {
843
-			if ($this->canAccessShare($share)) {
844
-				try {
845
-					$formatted[] = $this->formatShare($share);
846
-				} catch (NotFoundException $e) {
847
-					// Ignore this share
848
-				}
849
-			}
850
-		}
851
-
852
-		if ($includeTags) {
853
-			$formatted = $this->populateTags($formatted);
854
-		}
855
-
856
-		return $formatted;
857
-	}
858
-
859
-	/**
860
-	 * @param Node $folder
861
-	 *
862
-	 * @return list<Files_SharingShare>
863
-	 * @throws OCSBadRequestException
864
-	 * @throws NotFoundException
865
-	 */
866
-	private function getSharesInDir(Node $folder): array {
867
-		if (!($folder instanceof Folder)) {
868
-			throw new OCSBadRequestException($this->l->t('Not a directory'));
869
-		}
870
-
871
-		$nodes = $folder->getDirectoryListing();
872
-
873
-		/** @var IShare[] $shares */
874
-		$shares = array_reduce($nodes, function ($carry, $node) {
875
-			$carry = array_merge($carry, $this->getAllShares($node, true));
876
-			return $carry;
877
-		}, []);
878
-
879
-		// filter out duplicate shares
880
-		$known = [];
881
-
882
-		$formatted = $miniFormatted = [];
883
-		$resharingRight = false;
884
-		$known = [];
885
-		foreach ($shares as $share) {
886
-			if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->userId) {
887
-				continue;
888
-			}
889
-
890
-			try {
891
-				$format = $this->formatShare($share);
892
-
893
-				$known[] = $share->getId();
894
-				$formatted[] = $format;
895
-				if ($share->getSharedBy() === $this->userId) {
896
-					$miniFormatted[] = $format;
897
-				}
898
-				if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $folder)) {
899
-					$resharingRight = true;
900
-				}
901
-			} catch (\Exception $e) {
902
-				//Ignore this share
903
-			}
904
-		}
905
-
906
-		if (!$resharingRight) {
907
-			$formatted = $miniFormatted;
908
-		}
909
-
910
-		return $formatted;
911
-	}
912
-
913
-	/**
914
-	 * Get shares of the current user
915
-	 *
916
-	 * @param string $shared_with_me Only get shares with the current user
917
-	 * @param string $reshares Only get shares by the current user and reshares
918
-	 * @param string $subfiles Only get all shares in a folder
919
-	 * @param string $path Get shares for a specific path
920
-	 * @param string $include_tags Include tags in the share
921
-	 *
922
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
923
-	 * @throws OCSNotFoundException The folder was not found or is inaccessible
924
-	 *
925
-	 * 200: Shares returned
926
-	 */
927
-	#[NoAdminRequired]
928
-	public function getShares(
929
-		string $shared_with_me = 'false',
930
-		string $reshares = 'false',
931
-		string $subfiles = 'false',
932
-		string $path = '',
933
-		string $include_tags = 'false',
934
-	): DataResponse {
935
-		$node = null;
936
-		if ($path !== '') {
937
-			$userFolder = $this->rootFolder->getUserFolder($this->userId);
938
-			try {
939
-				$node = $userFolder->get($path);
940
-				$this->lock($node);
941
-			} catch (NotFoundException $e) {
942
-				throw new OCSNotFoundException(
943
-					$this->l->t('Wrong path, file/folder does not exist')
944
-				);
945
-			} catch (LockedException $e) {
946
-				throw new OCSNotFoundException($this->l->t('Could not lock node'));
947
-			}
948
-		}
949
-
950
-		$shares = $this->getFormattedShares(
951
-			$this->userId,
952
-			$node,
953
-			($shared_with_me === 'true'),
954
-			($reshares === 'true'),
955
-			($subfiles === 'true'),
956
-			($include_tags === 'true')
957
-		);
958
-
959
-		return new DataResponse($shares);
960
-	}
961
-
962
-	private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicUpload): int {
963
-		$permissions = $permissions ?? Constants::PERMISSION_READ;
964
-
965
-		// Legacy option handling
966
-		if ($legacyPublicUpload !== null) {
967
-			$permissions = $legacyPublicUpload
968
-				? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
969
-				: Constants::PERMISSION_READ;
970
-		}
971
-
972
-		// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
973
-		if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
974
-			&& $this->shareManager->outgoingServer2ServerSharesAllowed()) {
975
-			$permissions |= Constants::PERMISSION_SHARE;
976
-		}
977
-
978
-		return $permissions;
979
-	}
980
-
981
-	/**
982
-	 * Helper to check for legacy "publicUpload" handling.
983
-	 * If the value is set to `true` or `false` then true or false are returned.
984
-	 * Otherwise null is returned to indicate that the option was not (or wrong) set.
985
-	 *
986
-	 * @param null|string $legacyPublicUpload The value of `publicUpload`
987
-	 */
988
-	private function getLegacyPublicUpload(?string $legacyPublicUpload): ?bool {
989
-		if ($legacyPublicUpload === 'true') {
990
-			return true;
991
-		} elseif ($legacyPublicUpload === 'false') {
992
-			return false;
993
-		}
994
-		// Not set at all
995
-		return null;
996
-	}
997
-
998
-	/**
999
-	 * For link and email shares validate that only allowed combinations are set.
1000
-	 *
1001
-	 * @throw OCSBadRequestException If permission combination is invalid.
1002
-	 * @throw OCSForbiddenException If public upload was forbidden by the administrator.
1003
-	 */
1004
-	private function validateLinkSharePermissions(Node $node, int $permissions, ?bool $legacyPublicUpload): void {
1005
-		if ($legacyPublicUpload && ($node instanceof File)) {
1006
-			throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1007
-		}
1008
-
1009
-		// We need at least READ or CREATE (file drop)
1010
-		if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1011
-			&& !$this->hasPermission($permissions, Constants::PERMISSION_CREATE)) {
1012
-			throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1013
-		}
1014
-
1015
-		// UPDATE and DELETE require a READ permission
1016
-		if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1017
-			&& ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
1018
-			throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1019
-		}
1020
-
1021
-		// Check if public uploading was disabled
1022
-		if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1023
-			&& !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1024
-			throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1025
-		}
1026
-	}
1027
-
1028
-	/**
1029
-	 * @param string $viewer
1030
-	 * @param Node $node
1031
-	 * @param bool $sharedWithMe
1032
-	 * @param bool $reShares
1033
-	 * @param bool $subFiles
1034
-	 * @param bool $includeTags
1035
-	 *
1036
-	 * @return list<Files_SharingShare>
1037
-	 * @throws NotFoundException
1038
-	 * @throws OCSBadRequestException
1039
-	 */
1040
-	private function getFormattedShares(
1041
-		string $viewer,
1042
-		$node = null,
1043
-		bool $sharedWithMe = false,
1044
-		bool $reShares = false,
1045
-		bool $subFiles = false,
1046
-		bool $includeTags = false,
1047
-	): array {
1048
-		if ($sharedWithMe) {
1049
-			return $this->getSharedWithMe($node, $includeTags);
1050
-		}
1051
-
1052
-		if ($subFiles) {
1053
-			return $this->getSharesInDir($node);
1054
-		}
1055
-
1056
-		$shares = $this->getSharesFromNode($viewer, $node, $reShares);
1057
-
1058
-		$known = $formatted = $miniFormatted = [];
1059
-		$resharingRight = false;
1060
-		foreach ($shares as $share) {
1061
-			try {
1062
-				$share->getNode();
1063
-			} catch (NotFoundException $e) {
1064
-				/*
76
+    private ?Node $lockedNode = null;
77
+
78
+    /**
79
+     * Share20OCS constructor.
80
+     */
81
+    public function __construct(
82
+        string $appName,
83
+        IRequest $request,
84
+        private IManager $shareManager,
85
+        private IGroupManager $groupManager,
86
+        private IUserManager $userManager,
87
+        private IRootFolder $rootFolder,
88
+        private IURLGenerator $urlGenerator,
89
+        private IL10N $l,
90
+        private IConfig $config,
91
+        private IAppManager $appManager,
92
+        private ContainerInterface $serverContainer,
93
+        private IUserStatusManager $userStatusManager,
94
+        private IPreview $previewManager,
95
+        private IDateTimeZone $dateTimeZone,
96
+        private LoggerInterface $logger,
97
+        private IProviderFactory $factory,
98
+        private IMailer $mailer,
99
+        private ITagManager $tagManager,
100
+        private ?string $userId = null,
101
+    ) {
102
+        parent::__construct($appName, $request);
103
+    }
104
+
105
+    /**
106
+     * Convert an IShare to an array for OCS output
107
+     *
108
+     * @param IShare $share
109
+     * @param Node|null $recipientNode
110
+     * @return Files_SharingShare
111
+     * @throws NotFoundException In case the node can't be resolved.
112
+     *
113
+     * @suppress PhanUndeclaredClassMethod
114
+     */
115
+    protected function formatShare(IShare $share, ?Node $recipientNode = null): array {
116
+        $sharedBy = $this->userManager->get($share->getSharedBy());
117
+        $shareOwner = $this->userManager->get($share->getShareOwner());
118
+
119
+        $isOwnShare = false;
120
+        if ($shareOwner !== null) {
121
+            $isOwnShare = $shareOwner->getUID() === $this->userId;
122
+        }
123
+
124
+        $result = [
125
+            'id' => $share->getId(),
126
+            'share_type' => $share->getShareType(),
127
+            'uid_owner' => $share->getSharedBy(),
128
+            'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
129
+            // recipient permissions
130
+            'permissions' => $share->getPermissions(),
131
+            // current user permissions on this share
132
+            'can_edit' => $this->canEditShare($share),
133
+            'can_delete' => $this->canDeleteShare($share),
134
+            'stime' => $share->getShareTime()->getTimestamp(),
135
+            'parent' => null,
136
+            'expiration' => null,
137
+            'token' => null,
138
+            'uid_file_owner' => $share->getShareOwner(),
139
+            'note' => $share->getNote(),
140
+            'label' => $share->getLabel(),
141
+            'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
142
+        ];
143
+
144
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
145
+        if ($recipientNode) {
146
+            $node = $recipientNode;
147
+        } else {
148
+            $node = $userFolder->getFirstNodeById($share->getNodeId());
149
+            if (!$node) {
150
+                // fallback to guessing the path
151
+                $node = $userFolder->get($share->getTarget());
152
+                if ($node === null || $share->getTarget() === '') {
153
+                    throw new NotFoundException();
154
+                }
155
+            }
156
+        }
157
+
158
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
159
+        if ($node instanceof Folder) {
160
+            $result['item_type'] = 'folder';
161
+        } else {
162
+            $result['item_type'] = 'file';
163
+        }
164
+
165
+        // Get the original node permission if the share owner is the current user
166
+        if ($isOwnShare) {
167
+            $result['item_permissions'] = $node->getPermissions();
168
+        }
169
+
170
+        // If we're on the recipient side, the node permissions
171
+        // are bound to the share permissions. So we need to
172
+        // adjust the permissions to the share permissions if necessary.
173
+        if (!$isOwnShare) {
174
+            $result['item_permissions'] = $share->getPermissions();
175
+
176
+            // For some reason, single files share are forbidden to have the delete permission
177
+            // since we have custom methods to check those, let's adjust straight away.
178
+            // DAV permissions does not have that issue though.
179
+            if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
180
+                $result['item_permissions'] |= Constants::PERMISSION_DELETE;
181
+            }
182
+            if ($this->canEditShare($share)) {
183
+                $result['item_permissions'] |= Constants::PERMISSION_UPDATE;
184
+            }
185
+        }
186
+
187
+        // See MOUNT_ROOT_PROPERTYNAME dav property
188
+        $result['is-mount-root'] = $node->getInternalPath() === '';
189
+        $result['mount-type'] = $node->getMountPoint()->getMountType();
190
+
191
+        $result['mimetype'] = $node->getMimetype();
192
+        $result['has_preview'] = $this->previewManager->isAvailable($node);
193
+        $result['storage_id'] = $node->getStorage()->getId();
194
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
195
+        $result['item_source'] = $node->getId();
196
+        $result['file_source'] = $node->getId();
197
+        $result['file_parent'] = $node->getParent()->getId();
198
+        $result['file_target'] = $share->getTarget();
199
+        $result['item_size'] = $node->getSize();
200
+        $result['item_mtime'] = $node->getMTime();
201
+
202
+        $expiration = $share->getExpirationDate();
203
+        if ($expiration !== null) {
204
+            $expiration->setTimezone($this->dateTimeZone->getTimeZone());
205
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
206
+        }
207
+
208
+        if ($share->getShareType() === IShare::TYPE_USER) {
209
+            $sharedWith = $this->userManager->get($share->getSharedWith());
210
+            $result['share_with'] = $share->getSharedWith();
211
+            $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
212
+            $result['share_with_displayname_unique'] = $sharedWith !== null ? (
213
+                !empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
214
+            ) : $share->getSharedWith();
215
+
216
+            $userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
217
+            $userStatus = array_shift($userStatuses);
218
+            if ($userStatus) {
219
+                $result['status'] = [
220
+                    'status' => $userStatus->getStatus(),
221
+                    'message' => $userStatus->getMessage(),
222
+                    'icon' => $userStatus->getIcon(),
223
+                    'clearAt' => $userStatus->getClearAt()
224
+                        ? (int)$userStatus->getClearAt()->format('U')
225
+                        : null,
226
+                ];
227
+            }
228
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
229
+            $group = $this->groupManager->get($share->getSharedWith());
230
+            $result['share_with'] = $share->getSharedWith();
231
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
232
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
233
+
234
+            // "share_with" and "share_with_displayname" for passwords of link
235
+            // shares was deprecated in Nextcloud 15, use "password" instead.
236
+            $result['share_with'] = $share->getPassword();
237
+            $result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
238
+
239
+            $result['password'] = $share->getPassword();
240
+
241
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
242
+
243
+            $result['token'] = $share->getToken();
244
+            $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
245
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
246
+            $result['share_with'] = $share->getSharedWith();
247
+            $result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
248
+            $result['token'] = $share->getToken();
249
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
250
+            $result['share_with'] = $share->getSharedWith();
251
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
252
+            $result['token'] = $share->getToken();
253
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
254
+            $result['share_with'] = $share->getSharedWith();
255
+            $result['password'] = $share->getPassword();
256
+            $result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
257
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
258
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
259
+            $result['token'] = $share->getToken();
260
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
261
+            // getSharedWith() returns either "name (type, owner)" or
262
+            // "name (type, owner) [id]", depending on the Teams app version.
263
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
264
+
265
+            $result['share_with_displayname'] = $share->getSharedWithDisplayName();
266
+            if (empty($result['share_with_displayname'])) {
267
+                $displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
268
+                $result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
269
+            }
270
+
271
+            $result['share_with_avatar'] = $share->getSharedWithAvatar();
272
+
273
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
274
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
275
+            if ($shareWithLength === false) {
276
+                $result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
277
+            } else {
278
+                $result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
279
+            }
280
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
281
+            $result['share_with'] = $share->getSharedWith();
282
+            $result['share_with_displayname'] = '';
283
+
284
+            try {
285
+                /** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
286
+                $roomShare = $this->getRoomShareHelper()->formatShare($share);
287
+                $result = array_merge($result, $roomShare);
288
+            } catch (ContainerExceptionInterface $e) {
289
+            }
290
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
291
+            $result['share_with'] = $share->getSharedWith();
292
+            $result['share_with_displayname'] = '';
293
+
294
+            try {
295
+                /** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
296
+                $deckShare = $this->getDeckShareHelper()->formatShare($share);
297
+                $result = array_merge($result, $deckShare);
298
+            } catch (ContainerExceptionInterface $e) {
299
+            }
300
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
301
+            $result['share_with'] = $share->getSharedWith();
302
+            $result['share_with_displayname'] = '';
303
+
304
+            try {
305
+                /** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
306
+                $scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
307
+                $result = array_merge($result, $scienceMeshShare);
308
+            } catch (ContainerExceptionInterface $e) {
309
+            }
310
+        }
311
+
312
+
313
+        $result['mail_send'] = $share->getMailSend() ? 1 : 0;
314
+        $result['hide_download'] = $share->getHideDownload() ? 1 : 0;
315
+
316
+        $result['attributes'] = null;
317
+        if ($attributes = $share->getAttributes()) {
318
+            $result['attributes'] = (string)\json_encode($attributes->toArray());
319
+        }
320
+
321
+        return $result;
322
+    }
323
+
324
+    /**
325
+     * Check if one of the users address books knows the exact property, if
326
+     * not we return the full name.
327
+     *
328
+     * @param string $query
329
+     * @param string $property
330
+     * @return string
331
+     */
332
+    private function getDisplayNameFromAddressBook(string $query, string $property): string {
333
+        // FIXME: If we inject the contacts manager it gets initialized before any address books are registered
334
+        try {
335
+            $result = Server::get(\OCP\Contacts\IManager::class)->search($query, [$property], [
336
+                'limit' => 1,
337
+                'enumeration' => false,
338
+                'strict_search' => true,
339
+            ]);
340
+        } catch (Exception $e) {
341
+            $this->logger->error(
342
+                $e->getMessage(),
343
+                ['exception' => $e]
344
+            );
345
+            return $query;
346
+        }
347
+
348
+        foreach ($result as $r) {
349
+            foreach ($r[$property] as $value) {
350
+                if ($value === $query && $r['FN']) {
351
+                    return $r['FN'];
352
+                }
353
+            }
354
+        }
355
+
356
+        return $query;
357
+    }
358
+
359
+
360
+    /**
361
+     * @param list<Files_SharingShare> $shares
362
+     * @param array<string, string>|null $updatedDisplayName
363
+     *
364
+     * @return list<Files_SharingShare>
365
+     */
366
+    private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
367
+        $userIds = $updated = [];
368
+        foreach ($shares as $share) {
369
+            // share is federated and share have no display name yet
370
+            if ($share['share_type'] === IShare::TYPE_REMOTE
371
+                && ($share['share_with'] ?? '') !== ''
372
+                && ($share['share_with_displayname'] ?? '') === '') {
373
+                $userIds[] = $userId = $share['share_with'];
374
+
375
+                if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
376
+                    $share['share_with_displayname'] = $updatedDisplayName[$userId];
377
+                }
378
+            }
379
+
380
+            // prepping userIds with displayName to be updated
381
+            $updated[] = $share;
382
+        }
383
+
384
+        // if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
385
+        if ($updatedDisplayName !== null) {
386
+            return $updated;
387
+        }
388
+
389
+        // get displayName for the generated list of userId with no displayName
390
+        $displayNames = $this->retrieveFederatedDisplayName($userIds);
391
+
392
+        // if no displayName are updated, we exit
393
+        if (empty($displayNames)) {
394
+            return $updated;
395
+        }
396
+
397
+        // let's fix missing display name and returns all shares
398
+        return $this->fixMissingDisplayName($shares, $displayNames);
399
+    }
400
+
401
+
402
+    /**
403
+     * get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
404
+     * returns an array with userIds as keys and displayName as values.
405
+     *
406
+     * @param array $userIds
407
+     * @param bool $cacheOnly - do not reach LUS, get data from cache.
408
+     *
409
+     * @return array
410
+     * @throws ContainerExceptionInterface
411
+     */
412
+    private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
413
+        // check if gss is enabled and available
414
+        if (count($userIds) === 0
415
+            || !$this->appManager->isEnabledForAnyone('globalsiteselector')
416
+            || !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
417
+            return [];
418
+        }
419
+
420
+        try {
421
+            $slaveService = Server::get(SlaveService::class);
422
+        } catch (\Throwable $e) {
423
+            $this->logger->error(
424
+                $e->getMessage(),
425
+                ['exception' => $e]
426
+            );
427
+            return [];
428
+        }
429
+
430
+        return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
431
+    }
432
+
433
+
434
+    /**
435
+     * retrieve displayName from cache if available (should be used on federated shares)
436
+     * if not available in cache/lus, try for get from address-book, else returns empty string.
437
+     *
438
+     * @param string $userId
439
+     * @param bool $cacheOnly if true will not reach the lus but will only get data from cache
440
+     *
441
+     * @return string
442
+     */
443
+    private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
444
+        $details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
445
+        if (array_key_exists($userId, $details)) {
446
+            return $details[$userId];
447
+        }
448
+
449
+        $displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
450
+        return ($displayName === $userId) ? '' : $displayName;
451
+    }
452
+
453
+
454
+
455
+    /**
456
+     * Get a specific share by id
457
+     *
458
+     * @param string $id ID of the share
459
+     * @param bool $include_tags Include tags in the share
460
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
461
+     * @throws OCSNotFoundException Share not found
462
+     *
463
+     * 200: Share returned
464
+     */
465
+    #[NoAdminRequired]
466
+    public function getShare(string $id, bool $include_tags = false): DataResponse {
467
+        try {
468
+            $share = $this->getShareById($id);
469
+        } catch (ShareNotFound $e) {
470
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
471
+        }
472
+
473
+        try {
474
+            if ($this->canAccessShare($share)) {
475
+                $share = $this->formatShare($share);
476
+
477
+                if ($include_tags) {
478
+                    $share = $this->populateTags([$share]);
479
+                } else {
480
+                    $share = [$share];
481
+                }
482
+
483
+                return new DataResponse($share);
484
+            }
485
+        } catch (NotFoundException $e) {
486
+            // Fall through
487
+        }
488
+
489
+        throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
490
+    }
491
+
492
+    /**
493
+     * Delete a share
494
+     *
495
+     * @param string $id ID of the share
496
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
497
+     * @throws OCSNotFoundException Share not found
498
+     * @throws OCSForbiddenException Missing permissions to delete the share
499
+     *
500
+     * 200: Share deleted successfully
501
+     */
502
+    #[NoAdminRequired]
503
+    public function deleteShare(string $id): DataResponse {
504
+        try {
505
+            $share = $this->getShareById($id);
506
+        } catch (ShareNotFound $e) {
507
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
508
+        }
509
+
510
+        try {
511
+            $this->lock($share->getNode());
512
+        } catch (LockedException $e) {
513
+            throw new OCSNotFoundException($this->l->t('Could not delete share'));
514
+        }
515
+
516
+        if (!$this->canAccessShare($share)) {
517
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
518
+        }
519
+
520
+        // if it's a group share or a room share
521
+        // we don't delete the share, but only the
522
+        // mount point. Allowing it to be restored
523
+        // from the deleted shares
524
+        if ($this->canDeleteShareFromSelf($share)) {
525
+            $this->shareManager->deleteFromSelf($share, $this->userId);
526
+        } else {
527
+            if (!$this->canDeleteShare($share)) {
528
+                throw new OCSForbiddenException($this->l->t('Could not delete share'));
529
+            }
530
+
531
+            $this->shareManager->deleteShare($share);
532
+        }
533
+
534
+        return new DataResponse();
535
+    }
536
+
537
+    /**
538
+     * Create a share
539
+     *
540
+     * @param string|null $path Path of the share
541
+     * @param int|null $permissions Permissions for the share
542
+     * @param int $shareType Type of the share
543
+     * @param ?string $shareWith The entity this should be shared with
544
+     * @param 'true'|'false'|null $publicUpload If public uploading is allowed (deprecated)
545
+     * @param string $password Password for the share
546
+     * @param string|null $sendPasswordByTalk Send the password for the share over Talk
547
+     * @param ?string $expireDate The expiry date of the share in the user's timezone at 00:00.
548
+     *                            If $expireDate is not supplied or set to `null`, the system default will be used.
549
+     * @param string $note Note for the share
550
+     * @param string $label Label for the share (only used in link and email)
551
+     * @param string|null $attributes Additional attributes for the share
552
+     * @param 'false'|'true'|null $sendMail Send a mail to the recipient
553
+     *
554
+     * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
555
+     * @throws OCSBadRequestException Unknown share type
556
+     * @throws OCSException
557
+     * @throws OCSForbiddenException Creating the share is not allowed
558
+     * @throws OCSNotFoundException Creating the share failed
559
+     * @suppress PhanUndeclaredClassMethod
560
+     *
561
+     * 200: Share created
562
+     */
563
+    #[NoAdminRequired]
564
+    #[UserRateLimit(limit: 20, period: 600)]
565
+    public function createShare(
566
+        ?string $path = null,
567
+        ?int $permissions = null,
568
+        int $shareType = -1,
569
+        ?string $shareWith = null,
570
+        ?string $publicUpload = null,
571
+        string $password = '',
572
+        ?string $sendPasswordByTalk = null,
573
+        ?string $expireDate = null,
574
+        string $note = '',
575
+        string $label = '',
576
+        ?string $attributes = null,
577
+        ?string $sendMail = null,
578
+    ): DataResponse {
579
+        assert($this->userId !== null);
580
+
581
+        $share = $this->shareManager->newShare();
582
+        $hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
583
+
584
+        // Verify path
585
+        if ($path === null) {
586
+            throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
587
+        }
588
+
589
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
590
+        try {
591
+            /** @var \OC\Files\Node\Node $node */
592
+            $node = $userFolder->get($path);
593
+        } catch (NotFoundException $e) {
594
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
595
+        }
596
+
597
+        // a user can have access to a file through different paths, with differing permissions
598
+        // combine all permissions to determine if the user can share this file
599
+        $nodes = $userFolder->getById($node->getId());
600
+        foreach ($nodes as $nodeById) {
601
+            /** @var FileInfo $fileInfo */
602
+            $fileInfo = $node->getFileInfo();
603
+            $fileInfo['permissions'] |= $nodeById->getPermissions();
604
+        }
605
+
606
+        $share->setNode($node);
607
+
608
+        try {
609
+            $this->lock($share->getNode());
610
+        } catch (LockedException $e) {
611
+            throw new OCSNotFoundException($this->l->t('Could not create share'));
612
+        }
613
+
614
+        // Set permissions
615
+        if ($shareType === IShare::TYPE_LINK || $shareType === IShare::TYPE_EMAIL) {
616
+            $permissions = $this->getLinkSharePermissions($permissions, $hasPublicUpload);
617
+            $this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
618
+        } else {
619
+            // Use default permissions only for non-link shares to keep legacy behavior
620
+            if ($permissions === null) {
621
+                $permissions = (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
622
+            }
623
+            // Non-link shares always require read permissions (link shares could be file drop)
624
+            $permissions |= Constants::PERMISSION_READ;
625
+        }
626
+
627
+        // For legacy reasons the API allows to pass PERMISSIONS_ALL even for single file shares (I look at you Talk)
628
+        if ($node instanceof File) {
629
+            // if this is a single file share we remove the DELETE and CREATE permissions
630
+            $permissions = $permissions & ~(Constants::PERMISSION_DELETE | Constants::PERMISSION_CREATE);
631
+        }
632
+
633
+        /**
634
+         * Hack for https://github.com/owncloud/core/issues/22587
635
+         * We check the permissions via webdav. But the permissions of the mount point
636
+         * do not equal the share permissions. Here we fix that for federated mounts.
637
+         */
638
+        if ($node->getStorage()->instanceOfStorage(Storage::class)) {
639
+            $permissions &= ~($permissions & ~$node->getPermissions());
640
+        }
641
+
642
+        if ($attributes !== null) {
643
+            $share = $this->setShareAttributes($share, $attributes);
644
+        }
645
+
646
+        // Expire date checks
647
+        // Normally, null means no expiration date but we still set the default for backwards compatibility
648
+        // If the client sends an empty string, we set noExpirationDate to true
649
+        if ($expireDate !== null) {
650
+            if ($expireDate !== '') {
651
+                try {
652
+                    $expireDateTime = $this->parseDate($expireDate);
653
+                    $share->setExpirationDate($expireDateTime);
654
+                } catch (\Exception $e) {
655
+                    throw new OCSNotFoundException($e->getMessage(), $e);
656
+                }
657
+            } else {
658
+                // Client sent empty string for expire date.
659
+                // Set noExpirationDate to true so overwrite is prevented.
660
+                $share->setNoExpirationDate(true);
661
+            }
662
+        }
663
+
664
+        $share->setSharedBy($this->userId);
665
+
666
+        // Handle mail send
667
+        if (is_null($sendMail)) {
668
+            $allowSendMail = $this->config->getSystemValueBool('sharing.enable_share_mail', true);
669
+            if ($allowSendMail !== true || $shareType === IShare::TYPE_EMAIL) {
670
+                // Define a default behavior when sendMail is not provided
671
+                // For email shares with a valid recipient, the default is to send the mail
672
+                // For all other share types, the default is to not send the mail
673
+                $allowSendMail = ($shareType === IShare::TYPE_EMAIL && $shareWith !== null && $shareWith !== '');
674
+            }
675
+            $share->setMailSend($allowSendMail);
676
+        } else {
677
+            $share->setMailSend($sendMail === 'true');
678
+        }
679
+
680
+        if ($shareType === IShare::TYPE_USER) {
681
+            // Valid user is required to share
682
+            if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
683
+                throw new OCSNotFoundException($this->l->t('Please specify a valid account to share with'));
684
+            }
685
+            $share->setSharedWith($shareWith);
686
+            $share->setPermissions($permissions);
687
+        } elseif ($shareType === IShare::TYPE_GROUP) {
688
+            if (!$this->shareManager->allowGroupSharing()) {
689
+                throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
690
+            }
691
+
692
+            // Valid group is required to share
693
+            if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
694
+                throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
695
+            }
696
+            $share->setSharedWith($shareWith);
697
+            $share->setPermissions($permissions);
698
+        } elseif ($shareType === IShare::TYPE_LINK
699
+            || $shareType === IShare::TYPE_EMAIL) {
700
+
701
+            // Can we even share links?
702
+            if (!$this->shareManager->shareApiAllowLinks()) {
703
+                throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
704
+            }
705
+
706
+            $this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
707
+            $share->setPermissions($permissions);
708
+
709
+            // Set password
710
+            if ($password !== '') {
711
+                $share->setPassword($password);
712
+            }
713
+
714
+            // Only share by mail have a recipient
715
+            if (is_string($shareWith) && $shareType === IShare::TYPE_EMAIL) {
716
+                // If sending a mail have been requested, validate the mail address
717
+                if ($share->getMailSend() && !$this->mailer->validateMailAddress($shareWith)) {
718
+                    throw new OCSNotFoundException($this->l->t('Please specify a valid email address'));
719
+                }
720
+                $share->setSharedWith($shareWith);
721
+            }
722
+
723
+            // If we have a label, use it
724
+            if ($label !== '') {
725
+                if (strlen($label) > 255) {
726
+                    throw new OCSBadRequestException('Maximum label length is 255');
727
+                }
728
+                $share->setLabel($label);
729
+            }
730
+
731
+            if ($sendPasswordByTalk === 'true') {
732
+                if (!$this->appManager->isEnabledForUser('spreed')) {
733
+                    throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$node->getPath()]));
734
+                }
735
+
736
+                $share->setSendPasswordByTalk(true);
737
+            }
738
+        } elseif ($shareType === IShare::TYPE_REMOTE) {
739
+            if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
740
+                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]));
741
+            }
742
+
743
+            if ($shareWith === null) {
744
+                throw new OCSNotFoundException($this->l->t('Please specify a valid federated account ID'));
745
+            }
746
+
747
+            $share->setSharedWith($shareWith);
748
+            $share->setPermissions($permissions);
749
+            $share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
750
+        } elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
751
+            if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
752
+                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]));
753
+            }
754
+
755
+            if ($shareWith === null) {
756
+                throw new OCSNotFoundException($this->l->t('Please specify a valid federated group ID'));
757
+            }
758
+
759
+            $share->setSharedWith($shareWith);
760
+            $share->setPermissions($permissions);
761
+        } elseif ($shareType === IShare::TYPE_CIRCLE) {
762
+            if (!Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
763
+                throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
764
+            }
765
+
766
+            $circle = Circles::detailsCircle($shareWith);
767
+
768
+            // Valid team is required to share
769
+            if ($circle === null) {
770
+                throw new OCSNotFoundException($this->l->t('Please specify a valid team'));
771
+            }
772
+            $share->setSharedWith($shareWith);
773
+            $share->setPermissions($permissions);
774
+        } elseif ($shareType === IShare::TYPE_ROOM) {
775
+            try {
776
+                $this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
777
+            } catch (ContainerExceptionInterface $e) {
778
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
779
+            }
780
+        } elseif ($shareType === IShare::TYPE_DECK) {
781
+            try {
782
+                $this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
783
+            } catch (ContainerExceptionInterface $e) {
784
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
785
+            }
786
+        } elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
787
+            try {
788
+                $this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
789
+            } catch (ContainerExceptionInterface $e) {
790
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
791
+            }
792
+        } else {
793
+            throw new OCSBadRequestException($this->l->t('Unknown share type'));
794
+        }
795
+
796
+        $share->setShareType($shareType);
797
+        $this->checkInheritedAttributes($share);
798
+
799
+        if ($note !== '') {
800
+            $share->setNote($note);
801
+        }
802
+
803
+        try {
804
+            $share = $this->shareManager->createShare($share);
805
+        } catch (HintException $e) {
806
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
807
+            throw new OCSException($e->getHint(), $code);
808
+        } catch (GenericShareException|\InvalidArgumentException $e) {
809
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
810
+            throw new OCSForbiddenException($e->getMessage(), $e);
811
+        } catch (\Exception $e) {
812
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
813
+            throw new OCSForbiddenException('Failed to create share.', $e);
814
+        }
815
+
816
+        $output = $this->formatShare($share);
817
+
818
+        return new DataResponse($output);
819
+    }
820
+
821
+    /**
822
+     * @param null|Node $node
823
+     * @param boolean $includeTags
824
+     *
825
+     * @return list<Files_SharingShare>
826
+     */
827
+    private function getSharedWithMe($node, bool $includeTags): array {
828
+        $userShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_USER, $node, -1, 0);
829
+        $groupShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_GROUP, $node, -1, 0);
830
+        $circleShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_CIRCLE, $node, -1, 0);
831
+        $roomShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_ROOM, $node, -1, 0);
832
+        $deckShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_DECK, $node, -1, 0);
833
+        $sciencemeshShares = $this->shareManager->getSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, $node, -1, 0);
834
+
835
+        $shares = array_merge($userShares, $groupShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares);
836
+
837
+        $filteredShares = array_filter($shares, function (IShare $share) {
838
+            return $share->getShareOwner() !== $this->userId;
839
+        });
840
+
841
+        $formatted = [];
842
+        foreach ($filteredShares as $share) {
843
+            if ($this->canAccessShare($share)) {
844
+                try {
845
+                    $formatted[] = $this->formatShare($share);
846
+                } catch (NotFoundException $e) {
847
+                    // Ignore this share
848
+                }
849
+            }
850
+        }
851
+
852
+        if ($includeTags) {
853
+            $formatted = $this->populateTags($formatted);
854
+        }
855
+
856
+        return $formatted;
857
+    }
858
+
859
+    /**
860
+     * @param Node $folder
861
+     *
862
+     * @return list<Files_SharingShare>
863
+     * @throws OCSBadRequestException
864
+     * @throws NotFoundException
865
+     */
866
+    private function getSharesInDir(Node $folder): array {
867
+        if (!($folder instanceof Folder)) {
868
+            throw new OCSBadRequestException($this->l->t('Not a directory'));
869
+        }
870
+
871
+        $nodes = $folder->getDirectoryListing();
872
+
873
+        /** @var IShare[] $shares */
874
+        $shares = array_reduce($nodes, function ($carry, $node) {
875
+            $carry = array_merge($carry, $this->getAllShares($node, true));
876
+            return $carry;
877
+        }, []);
878
+
879
+        // filter out duplicate shares
880
+        $known = [];
881
+
882
+        $formatted = $miniFormatted = [];
883
+        $resharingRight = false;
884
+        $known = [];
885
+        foreach ($shares as $share) {
886
+            if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->userId) {
887
+                continue;
888
+            }
889
+
890
+            try {
891
+                $format = $this->formatShare($share);
892
+
893
+                $known[] = $share->getId();
894
+                $formatted[] = $format;
895
+                if ($share->getSharedBy() === $this->userId) {
896
+                    $miniFormatted[] = $format;
897
+                }
898
+                if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $folder)) {
899
+                    $resharingRight = true;
900
+                }
901
+            } catch (\Exception $e) {
902
+                //Ignore this share
903
+            }
904
+        }
905
+
906
+        if (!$resharingRight) {
907
+            $formatted = $miniFormatted;
908
+        }
909
+
910
+        return $formatted;
911
+    }
912
+
913
+    /**
914
+     * Get shares of the current user
915
+     *
916
+     * @param string $shared_with_me Only get shares with the current user
917
+     * @param string $reshares Only get shares by the current user and reshares
918
+     * @param string $subfiles Only get all shares in a folder
919
+     * @param string $path Get shares for a specific path
920
+     * @param string $include_tags Include tags in the share
921
+     *
922
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
923
+     * @throws OCSNotFoundException The folder was not found or is inaccessible
924
+     *
925
+     * 200: Shares returned
926
+     */
927
+    #[NoAdminRequired]
928
+    public function getShares(
929
+        string $shared_with_me = 'false',
930
+        string $reshares = 'false',
931
+        string $subfiles = 'false',
932
+        string $path = '',
933
+        string $include_tags = 'false',
934
+    ): DataResponse {
935
+        $node = null;
936
+        if ($path !== '') {
937
+            $userFolder = $this->rootFolder->getUserFolder($this->userId);
938
+            try {
939
+                $node = $userFolder->get($path);
940
+                $this->lock($node);
941
+            } catch (NotFoundException $e) {
942
+                throw new OCSNotFoundException(
943
+                    $this->l->t('Wrong path, file/folder does not exist')
944
+                );
945
+            } catch (LockedException $e) {
946
+                throw new OCSNotFoundException($this->l->t('Could not lock node'));
947
+            }
948
+        }
949
+
950
+        $shares = $this->getFormattedShares(
951
+            $this->userId,
952
+            $node,
953
+            ($shared_with_me === 'true'),
954
+            ($reshares === 'true'),
955
+            ($subfiles === 'true'),
956
+            ($include_tags === 'true')
957
+        );
958
+
959
+        return new DataResponse($shares);
960
+    }
961
+
962
+    private function getLinkSharePermissions(?int $permissions, ?bool $legacyPublicUpload): int {
963
+        $permissions = $permissions ?? Constants::PERMISSION_READ;
964
+
965
+        // Legacy option handling
966
+        if ($legacyPublicUpload !== null) {
967
+            $permissions = $legacyPublicUpload
968
+                ? (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
969
+                : Constants::PERMISSION_READ;
970
+        }
971
+
972
+        // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
973
+        if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
974
+            && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
975
+            $permissions |= Constants::PERMISSION_SHARE;
976
+        }
977
+
978
+        return $permissions;
979
+    }
980
+
981
+    /**
982
+     * Helper to check for legacy "publicUpload" handling.
983
+     * If the value is set to `true` or `false` then true or false are returned.
984
+     * Otherwise null is returned to indicate that the option was not (or wrong) set.
985
+     *
986
+     * @param null|string $legacyPublicUpload The value of `publicUpload`
987
+     */
988
+    private function getLegacyPublicUpload(?string $legacyPublicUpload): ?bool {
989
+        if ($legacyPublicUpload === 'true') {
990
+            return true;
991
+        } elseif ($legacyPublicUpload === 'false') {
992
+            return false;
993
+        }
994
+        // Not set at all
995
+        return null;
996
+    }
997
+
998
+    /**
999
+     * For link and email shares validate that only allowed combinations are set.
1000
+     *
1001
+     * @throw OCSBadRequestException If permission combination is invalid.
1002
+     * @throw OCSForbiddenException If public upload was forbidden by the administrator.
1003
+     */
1004
+    private function validateLinkSharePermissions(Node $node, int $permissions, ?bool $legacyPublicUpload): void {
1005
+        if ($legacyPublicUpload && ($node instanceof File)) {
1006
+            throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1007
+        }
1008
+
1009
+        // We need at least READ or CREATE (file drop)
1010
+        if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1011
+            && !$this->hasPermission($permissions, Constants::PERMISSION_CREATE)) {
1012
+            throw new OCSBadRequestException($this->l->t('Share must at least have READ or CREATE permissions'));
1013
+        }
1014
+
1015
+        // UPDATE and DELETE require a READ permission
1016
+        if (!$this->hasPermission($permissions, Constants::PERMISSION_READ)
1017
+            && ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
1018
+            throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
1019
+        }
1020
+
1021
+        // Check if public uploading was disabled
1022
+        if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1023
+            && !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1024
+            throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1025
+        }
1026
+    }
1027
+
1028
+    /**
1029
+     * @param string $viewer
1030
+     * @param Node $node
1031
+     * @param bool $sharedWithMe
1032
+     * @param bool $reShares
1033
+     * @param bool $subFiles
1034
+     * @param bool $includeTags
1035
+     *
1036
+     * @return list<Files_SharingShare>
1037
+     * @throws NotFoundException
1038
+     * @throws OCSBadRequestException
1039
+     */
1040
+    private function getFormattedShares(
1041
+        string $viewer,
1042
+        $node = null,
1043
+        bool $sharedWithMe = false,
1044
+        bool $reShares = false,
1045
+        bool $subFiles = false,
1046
+        bool $includeTags = false,
1047
+    ): array {
1048
+        if ($sharedWithMe) {
1049
+            return $this->getSharedWithMe($node, $includeTags);
1050
+        }
1051
+
1052
+        if ($subFiles) {
1053
+            return $this->getSharesInDir($node);
1054
+        }
1055
+
1056
+        $shares = $this->getSharesFromNode($viewer, $node, $reShares);
1057
+
1058
+        $known = $formatted = $miniFormatted = [];
1059
+        $resharingRight = false;
1060
+        foreach ($shares as $share) {
1061
+            try {
1062
+                $share->getNode();
1063
+            } catch (NotFoundException $e) {
1064
+                /*
1065 1065
 				 * Ignore shares where we can't get the node
1066 1066
 				 * For example deleted shares
1067 1067
 				 */
1068
-				continue;
1069
-			}
1070
-
1071
-			if (in_array($share->getId(), $known)
1072
-				|| ($share->getSharedWith() === $this->userId && $share->getShareType() === IShare::TYPE_USER)) {
1073
-				continue;
1074
-			}
1075
-
1076
-			$known[] = $share->getId();
1077
-			try {
1078
-				/** @var IShare $share */
1079
-				$format = $this->formatShare($share, $node);
1080
-				$formatted[] = $format;
1081
-
1082
-				// let's also build a list of shares created
1083
-				// by the current user only, in case
1084
-				// there is no resharing rights
1085
-				if ($share->getSharedBy() === $this->userId) {
1086
-					$miniFormatted[] = $format;
1087
-				}
1088
-
1089
-				// check if one of those share is shared with me
1090
-				// and if I have resharing rights on it
1091
-				if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $node)) {
1092
-					$resharingRight = true;
1093
-				}
1094
-			} catch (InvalidPathException|NotFoundException $e) {
1095
-			}
1096
-		}
1097
-
1098
-		if (!$resharingRight) {
1099
-			$formatted = $miniFormatted;
1100
-		}
1101
-
1102
-		// fix eventual missing display name from federated shares
1103
-		$formatted = $this->fixMissingDisplayName($formatted);
1104
-
1105
-		if ($includeTags) {
1106
-			$formatted = $this->populateTags($formatted);
1107
-		}
1108
-
1109
-		return $formatted;
1110
-	}
1111
-
1112
-
1113
-	/**
1114
-	 * Get all shares relative to a file, including parent folders shares rights
1115
-	 *
1116
-	 * @param string $path Path all shares will be relative to
1117
-	 *
1118
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1119
-	 * @throws InvalidPathException
1120
-	 * @throws NotFoundException
1121
-	 * @throws OCSNotFoundException The given path is invalid
1122
-	 * @throws SharingRightsException
1123
-	 *
1124
-	 * 200: Shares returned
1125
-	 */
1126
-	#[NoAdminRequired]
1127
-	public function getInheritedShares(string $path): DataResponse {
1128
-		// get Node from (string) path.
1129
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1130
-		try {
1131
-			$node = $userFolder->get($path);
1132
-			$this->lock($node);
1133
-		} catch (NotFoundException $e) {
1134
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
1135
-		} catch (LockedException $e) {
1136
-			throw new OCSNotFoundException($this->l->t('Could not lock path'));
1137
-		}
1138
-
1139
-		if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
1140
-			throw new SharingRightsException($this->l->t('no sharing rights on this item'));
1141
-		}
1142
-
1143
-		// The current top parent we have access to
1144
-		$parent = $node;
1145
-
1146
-		// initiate real owner.
1147
-		$owner = $node->getOwner()
1148
-			->getUID();
1149
-		if (!$this->userManager->userExists($owner)) {
1150
-			return new DataResponse([]);
1151
-		}
1152
-
1153
-		// get node based on the owner, fix owner in case of external storage
1154
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1155
-		if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
1156
-			$owner = $node->getOwner()
1157
-				->getUID();
1158
-			$userFolder = $this->rootFolder->getUserFolder($owner);
1159
-			$node = $userFolder->getFirstNodeById($node->getId());
1160
-		}
1161
-		$basePath = $userFolder->getPath();
1162
-
1163
-		// generate node list for each parent folders
1164
-		/** @var Node[] $nodes */
1165
-		$nodes = [];
1166
-		while (true) {
1167
-			$node = $node->getParent();
1168
-			if ($node->getPath() === $basePath) {
1169
-				break;
1170
-			}
1171
-			$nodes[] = $node;
1172
-		}
1173
-
1174
-		// The user that is requesting this list
1175
-		$currentUserFolder = $this->rootFolder->getUserFolder($this->userId);
1176
-
1177
-		// for each nodes, retrieve shares.
1178
-		$shares = [];
1179
-
1180
-		foreach ($nodes as $node) {
1181
-			$getShares = $this->getFormattedShares($owner, $node, false, true);
1182
-
1183
-			$currentUserNode = $currentUserFolder->getFirstNodeById($node->getId());
1184
-			if ($currentUserNode) {
1185
-				$parent = $currentUserNode;
1186
-			}
1187
-
1188
-			$subPath = $currentUserFolder->getRelativePath($parent->getPath());
1189
-			foreach ($getShares as &$share) {
1190
-				$share['via_fileid'] = $parent->getId();
1191
-				$share['via_path'] = $subPath;
1192
-			}
1193
-			$this->mergeFormattedShares($shares, $getShares);
1194
-		}
1195
-
1196
-		return new DataResponse(array_values($shares));
1197
-	}
1198
-
1199
-	/**
1200
-	 * Check whether a set of permissions contains the permissions to check.
1201
-	 */
1202
-	private function hasPermission(int $permissionsSet, int $permissionsToCheck): bool {
1203
-		return ($permissionsSet & $permissionsToCheck) === $permissionsToCheck;
1204
-	}
1205
-
1206
-	/**
1207
-	 * Update a share
1208
-	 *
1209
-	 * @param string $id ID of the share
1210
-	 * @param int|null $permissions New permissions
1211
-	 * @param string|null $password New password
1212
-	 * @param string|null $sendPasswordByTalk New condition if the password should be send over Talk
1213
-	 * @param string|null $publicUpload New condition if public uploading is allowed
1214
-	 * @param string|null $expireDate New expiry date
1215
-	 * @param string|null $note New note
1216
-	 * @param string|null $label New label
1217
-	 * @param string|null $hideDownload New condition if the download should be hidden
1218
-	 * @param string|null $attributes New additional attributes
1219
-	 * @param string|null $sendMail if the share should be send by mail.
1220
-	 *                              Considering the share already exists, no mail will be send after the share is updated.
1221
-	 *                              You will have to use the sendMail action to send the mail.
1222
-	 * @param string|null $shareWith New recipient for email shares
1223
-	 * @param string|null $token New token
1224
-	 * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
1225
-	 * @throws OCSBadRequestException Share could not be updated because the requested changes are invalid
1226
-	 * @throws OCSForbiddenException Missing permissions to update the share
1227
-	 * @throws OCSNotFoundException Share not found
1228
-	 *
1229
-	 * 200: Share updated successfully
1230
-	 */
1231
-	#[NoAdminRequired]
1232
-	public function updateShare(
1233
-		string $id,
1234
-		?int $permissions = null,
1235
-		?string $password = null,
1236
-		?string $sendPasswordByTalk = null,
1237
-		?string $publicUpload = null,
1238
-		?string $expireDate = null,
1239
-		?string $note = null,
1240
-		?string $label = null,
1241
-		?string $hideDownload = null,
1242
-		?string $attributes = null,
1243
-		?string $sendMail = null,
1244
-		?string $token = null,
1245
-	): DataResponse {
1246
-		try {
1247
-			$share = $this->getShareById($id);
1248
-		} catch (ShareNotFound $e) {
1249
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1250
-		}
1251
-
1252
-		$this->lock($share->getNode());
1253
-
1254
-		if (!$this->canAccessShare($share, false)) {
1255
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1256
-		}
1257
-
1258
-		if (!$this->canEditShare($share)) {
1259
-			throw new OCSForbiddenException($this->l->t('You are not allowed to edit incoming shares'));
1260
-		}
1261
-
1262
-		if (
1263
-			$permissions === null
1264
-			&& $password === null
1265
-			&& $sendPasswordByTalk === null
1266
-			&& $publicUpload === null
1267
-			&& $expireDate === null
1268
-			&& $note === null
1269
-			&& $label === null
1270
-			&& $hideDownload === null
1271
-			&& $attributes === null
1272
-			&& $sendMail === null
1273
-			&& $token === null
1274
-		) {
1275
-			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
1276
-		}
1277
-
1278
-		if ($note !== null) {
1279
-			$share->setNote($note);
1280
-		}
1281
-
1282
-		if ($attributes !== null) {
1283
-			$share = $this->setShareAttributes($share, $attributes);
1284
-		}
1285
-
1286
-		// Handle mail send
1287
-		if ($sendMail === 'true' || $sendMail === 'false') {
1288
-			$share->setMailSend($sendMail === 'true');
1289
-		}
1290
-
1291
-		/**
1292
-		 * expiration date, password and publicUpload only make sense for link shares
1293
-		 */
1294
-		if ($share->getShareType() === IShare::TYPE_LINK
1295
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1296
-
1297
-			// Update hide download state
1298
-			if ($hideDownload === 'true') {
1299
-				$share->setHideDownload(true);
1300
-			} elseif ($hideDownload === 'false') {
1301
-				$share->setHideDownload(false);
1302
-			}
1303
-
1304
-			// If either manual permissions are specified or publicUpload
1305
-			// then we need to also update the permissions of the share
1306
-			if ($permissions !== null || $publicUpload !== null) {
1307
-				$hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
1308
-				$permissions = $this->getLinkSharePermissions($permissions ?? Constants::PERMISSION_READ, $hasPublicUpload);
1309
-				$this->validateLinkSharePermissions($share->getNode(), $permissions, $hasPublicUpload);
1310
-				$share->setPermissions($permissions);
1311
-			}
1312
-
1313
-			if ($password === '') {
1314
-				$share->setPassword(null);
1315
-			} elseif ($password !== null) {
1316
-				$share->setPassword($password);
1317
-			}
1318
-
1319
-			if ($label !== null) {
1320
-				if (strlen($label) > 255) {
1321
-					throw new OCSBadRequestException('Maximum label length is 255');
1322
-				}
1323
-				$share->setLabel($label);
1324
-			}
1325
-
1326
-			if ($sendPasswordByTalk === 'true') {
1327
-				if (!$this->appManager->isEnabledForUser('spreed')) {
1328
-					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.'));
1329
-				}
1330
-
1331
-				$share->setSendPasswordByTalk(true);
1332
-			} elseif ($sendPasswordByTalk !== null) {
1333
-				$share->setSendPasswordByTalk(false);
1334
-			}
1335
-
1336
-			if ($token !== null) {
1337
-				if (!$this->shareManager->allowCustomTokens()) {
1338
-					throw new OCSForbiddenException($this->l->t('Custom share link tokens have been disabled by the administrator'));
1339
-				}
1340
-				if (!$this->validateToken($token)) {
1341
-					throw new OCSBadRequestException($this->l->t('Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen'));
1342
-				}
1343
-				$share->setToken($token);
1344
-			}
1345
-		}
1346
-
1347
-		// NOT A LINK SHARE
1348
-		else {
1349
-			if ($permissions !== null) {
1350
-				$share->setPermissions($permissions);
1351
-			}
1352
-		}
1353
-
1354
-		if ($expireDate === '') {
1355
-			$share->setExpirationDate(null);
1356
-		} elseif ($expireDate !== null) {
1357
-			try {
1358
-				$expireDateTime = $this->parseDate($expireDate);
1359
-				$share->setExpirationDate($expireDateTime);
1360
-			} catch (\Exception $e) {
1361
-				throw new OCSBadRequestException($e->getMessage(), $e);
1362
-			}
1363
-		}
1364
-
1365
-		try {
1366
-			$this->checkInheritedAttributes($share);
1367
-			$share = $this->shareManager->updateShare($share);
1368
-		} catch (HintException $e) {
1369
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1370
-			throw new OCSException($e->getHint(), (int)$code);
1371
-		} catch (\Exception $e) {
1372
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
1373
-			throw new OCSBadRequestException('Failed to update share.', $e);
1374
-		}
1375
-
1376
-		return new DataResponse($this->formatShare($share));
1377
-	}
1378
-
1379
-	private function validateToken(string $token): bool {
1380
-		if (mb_strlen($token) === 0) {
1381
-			return false;
1382
-		}
1383
-		if (!preg_match('/^[a-z0-9-]+$/i', $token)) {
1384
-			return false;
1385
-		}
1386
-		return true;
1387
-	}
1388
-
1389
-	/**
1390
-	 * Get all shares that are still pending
1391
-	 *
1392
-	 * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1393
-	 *
1394
-	 * 200: Pending shares returned
1395
-	 */
1396
-	#[NoAdminRequired]
1397
-	public function pendingShares(): DataResponse {
1398
-		$pendingShares = [];
1399
-
1400
-		$shareTypes = [
1401
-			IShare::TYPE_USER,
1402
-			IShare::TYPE_GROUP
1403
-		];
1404
-
1405
-		foreach ($shareTypes as $shareType) {
1406
-			$shares = $this->shareManager->getSharedWith($this->userId, $shareType, null, -1, 0);
1407
-
1408
-			foreach ($shares as $share) {
1409
-				if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1410
-					$pendingShares[] = $share;
1411
-				}
1412
-			}
1413
-		}
1414
-
1415
-		$result = array_values(array_filter(array_map(function (IShare $share) {
1416
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1417
-			$node = $userFolder->getFirstNodeById($share->getNodeId());
1418
-			if (!$node) {
1419
-				// fallback to guessing the path
1420
-				$node = $userFolder->get($share->getTarget());
1421
-				if ($node === null || $share->getTarget() === '') {
1422
-					return null;
1423
-				}
1424
-			}
1425
-
1426
-			try {
1427
-				$formattedShare = $this->formatShare($share, $node);
1428
-				$formattedShare['path'] = '/' . $share->getNode()->getName();
1429
-				$formattedShare['permissions'] = 0;
1430
-				return $formattedShare;
1431
-			} catch (NotFoundException $e) {
1432
-				return null;
1433
-			}
1434
-		}, $pendingShares), function ($entry) {
1435
-			return $entry !== null;
1436
-		}));
1437
-
1438
-		return new DataResponse($result);
1439
-	}
1440
-
1441
-	/**
1442
-	 * Accept a share
1443
-	 *
1444
-	 * @param string $id ID of the share
1445
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1446
-	 * @throws OCSNotFoundException Share not found
1447
-	 * @throws OCSException
1448
-	 * @throws OCSBadRequestException Share could not be accepted
1449
-	 *
1450
-	 * 200: Share accepted successfully
1451
-	 */
1452
-	#[NoAdminRequired]
1453
-	public function acceptShare(string $id): DataResponse {
1454
-		try {
1455
-			$share = $this->getShareById($id);
1456
-		} catch (ShareNotFound $e) {
1457
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1458
-		}
1459
-
1460
-		if (!$this->canAccessShare($share)) {
1461
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1462
-		}
1463
-
1464
-		try {
1465
-			$this->shareManager->acceptShare($share, $this->userId);
1466
-		} catch (HintException $e) {
1467
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1468
-			throw new OCSException($e->getHint(), (int)$code);
1469
-		} catch (\Exception $e) {
1470
-			$this->logger->error($e->getMessage(), ['exception' => $e]);
1471
-			throw new OCSBadRequestException('Failed to accept share.', $e);
1472
-		}
1473
-
1474
-		return new DataResponse();
1475
-	}
1476
-
1477
-	/**
1478
-	 * Does the user have read permission on the share
1479
-	 *
1480
-	 * @param IShare $share the share to check
1481
-	 * @param boolean $checkGroups check groups as well?
1482
-	 * @return boolean
1483
-	 * @throws NotFoundException
1484
-	 *
1485
-	 * @suppress PhanUndeclaredClassMethod
1486
-	 */
1487
-	protected function canAccessShare(IShare $share, bool $checkGroups = true): bool {
1488
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1489
-		if ($share->getPermissions() === 0) {
1490
-			return false;
1491
-		}
1492
-
1493
-		// Owner of the file and the sharer of the file can always get share
1494
-		if ($share->getShareOwner() === $this->userId
1495
-			|| $share->getSharedBy() === $this->userId) {
1496
-			return true;
1497
-		}
1498
-
1499
-		// If the share is shared with you, you can access it!
1500
-		if ($share->getShareType() === IShare::TYPE_USER
1501
-			&& $share->getSharedWith() === $this->userId) {
1502
-			return true;
1503
-		}
1504
-
1505
-		// Have reshare rights on the shared file/folder ?
1506
-		// Does the currentUser have access to the shared file?
1507
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1508
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1509
-		if ($file && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1510
-			return true;
1511
-		}
1512
-
1513
-		// If in the recipient group, you can see the share
1514
-		if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1515
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1516
-			$user = $this->userManager->get($this->userId);
1517
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1518
-				return true;
1519
-			}
1520
-		}
1521
-
1522
-		if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1523
-			// TODO: have a sanity check like above?
1524
-			return true;
1525
-		}
1526
-
1527
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1528
-			try {
1529
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1530
-			} catch (ContainerExceptionInterface $e) {
1531
-				return false;
1532
-			}
1533
-		}
1534
-
1535
-		if ($share->getShareType() === IShare::TYPE_DECK) {
1536
-			try {
1537
-				return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1538
-			} catch (ContainerExceptionInterface $e) {
1539
-				return false;
1540
-			}
1541
-		}
1542
-
1543
-		if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1544
-			try {
1545
-				return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1546
-			} catch (ContainerExceptionInterface $e) {
1547
-				return false;
1548
-			}
1549
-		}
1550
-
1551
-		return false;
1552
-	}
1553
-
1554
-	/**
1555
-	 * Does the user have edit permission on the share
1556
-	 *
1557
-	 * @param IShare $share the share to check
1558
-	 * @return boolean
1559
-	 */
1560
-	protected function canEditShare(IShare $share): bool {
1561
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1562
-		if ($share->getPermissions() === 0) {
1563
-			return false;
1564
-		}
1565
-
1566
-		// The owner of the file and the creator of the share
1567
-		// can always edit the share
1568
-		if ($share->getShareOwner() === $this->userId
1569
-			|| $share->getSharedBy() === $this->userId
1570
-		) {
1571
-			return true;
1572
-		}
1573
-
1574
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1575
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1576
-		if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1577
-			return true;
1578
-		}
1579
-
1580
-		//! we do NOT support some kind of `admin` in groups.
1581
-		//! You cannot edit shares shared to a group you're
1582
-		//! a member of if you're not the share owner or the file owner!
1583
-
1584
-		return false;
1585
-	}
1586
-
1587
-	/**
1588
-	 * Does the user have delete permission on the share
1589
-	 *
1590
-	 * @param IShare $share the share to check
1591
-	 * @return boolean
1592
-	 */
1593
-	protected function canDeleteShare(IShare $share): bool {
1594
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1595
-		if ($share->getPermissions() === 0) {
1596
-			return false;
1597
-		}
1598
-
1599
-		// if the user is the recipient, i can unshare
1600
-		// the share with self
1601
-		if ($share->getShareType() === IShare::TYPE_USER
1602
-			&& $share->getSharedWith() === $this->userId
1603
-		) {
1604
-			return true;
1605
-		}
1606
-
1607
-		// The owner of the file and the creator of the share
1608
-		// can always delete the share
1609
-		if ($share->getShareOwner() === $this->userId
1610
-			|| $share->getSharedBy() === $this->userId
1611
-		) {
1612
-			return true;
1613
-		}
1614
-
1615
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
1616
-		$file = $userFolder->getFirstNodeById($share->getNodeId());
1617
-		if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1618
-			return true;
1619
-		}
1620
-
1621
-		return false;
1622
-	}
1623
-
1624
-	/**
1625
-	 * Does the user have delete permission on the share
1626
-	 * This differs from the canDeleteShare function as it only
1627
-	 * remove the share for the current user. It does NOT
1628
-	 * completely delete the share but only the mount point.
1629
-	 * It can then be restored from the deleted shares section.
1630
-	 *
1631
-	 * @param IShare $share the share to check
1632
-	 * @return boolean
1633
-	 *
1634
-	 * @suppress PhanUndeclaredClassMethod
1635
-	 */
1636
-	protected function canDeleteShareFromSelf(IShare $share): bool {
1637
-		if ($share->getShareType() !== IShare::TYPE_GROUP
1638
-			&& $share->getShareType() !== IShare::TYPE_ROOM
1639
-			&& $share->getShareType() !== IShare::TYPE_DECK
1640
-			&& $share->getShareType() !== IShare::TYPE_SCIENCEMESH
1641
-		) {
1642
-			return false;
1643
-		}
1644
-
1645
-		if ($share->getShareOwner() === $this->userId
1646
-			|| $share->getSharedBy() === $this->userId
1647
-		) {
1648
-			// Delete the whole share, not just for self
1649
-			return false;
1650
-		}
1651
-
1652
-		// If in the recipient group, you can delete the share from self
1653
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1654
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1655
-			$user = $this->userManager->get($this->userId);
1656
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1657
-				return true;
1658
-			}
1659
-		}
1660
-
1661
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1662
-			try {
1663
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1664
-			} catch (ContainerExceptionInterface $e) {
1665
-				return false;
1666
-			}
1667
-		}
1668
-
1669
-		if ($share->getShareType() === IShare::TYPE_DECK) {
1670
-			try {
1671
-				return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1672
-			} catch (ContainerExceptionInterface $e) {
1673
-				return false;
1674
-			}
1675
-		}
1676
-
1677
-		if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1678
-			try {
1679
-				return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1680
-			} catch (ContainerExceptionInterface $e) {
1681
-				return false;
1682
-			}
1683
-		}
1684
-
1685
-		return false;
1686
-	}
1687
-
1688
-	/**
1689
-	 * Make sure that the passed date is valid ISO 8601
1690
-	 * So YYYY-MM-DD
1691
-	 * If not throw an exception
1692
-	 *
1693
-	 * @param string $expireDate
1694
-	 *
1695
-	 * @throws \Exception
1696
-	 * @return \DateTime
1697
-	 */
1698
-	private function parseDate(string $expireDate): \DateTime {
1699
-		try {
1700
-			$date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
1701
-			// Make sure it expires at midnight in owner timezone
1702
-			$date->setTime(0, 0, 0);
1703
-		} catch (\Exception $e) {
1704
-			throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
1705
-		}
1706
-
1707
-		return $date;
1708
-	}
1709
-
1710
-	/**
1711
-	 * Since we have multiple providers but the OCS Share API v1 does
1712
-	 * not support this we need to check all backends.
1713
-	 *
1714
-	 * @param string $id
1715
-	 * @return IShare
1716
-	 * @throws ShareNotFound
1717
-	 */
1718
-	private function getShareById(string $id): IShare {
1719
-		$share = null;
1720
-
1721
-		// First check if it is an internal share.
1722
-		try {
1723
-			$share = $this->shareManager->getShareById('ocinternal:' . $id, $this->userId);
1724
-			return $share;
1725
-		} catch (ShareNotFound $e) {
1726
-			// Do nothing, just try the other share type
1727
-		}
1728
-
1729
-
1730
-		try {
1731
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_CIRCLE)) {
1732
-				$share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->userId);
1733
-				return $share;
1734
-			}
1735
-		} catch (ShareNotFound $e) {
1736
-			// Do nothing, just try the other share type
1737
-		}
1738
-
1739
-		try {
1740
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
1741
-				$share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->userId);
1742
-				return $share;
1743
-			}
1744
-		} catch (ShareNotFound $e) {
1745
-			// Do nothing, just try the other share type
1746
-		}
1747
-
1748
-		try {
1749
-			$share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->userId);
1750
-			return $share;
1751
-		} catch (ShareNotFound $e) {
1752
-			// Do nothing, just try the other share type
1753
-		}
1754
-
1755
-		try {
1756
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) {
1757
-				$share = $this->shareManager->getShareById('deck:' . $id, $this->userId);
1758
-				return $share;
1759
-			}
1760
-		} catch (ShareNotFound $e) {
1761
-			// Do nothing, just try the other share type
1762
-		}
1763
-
1764
-		try {
1765
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_SCIENCEMESH)) {
1766
-				$share = $this->shareManager->getShareById('sciencemesh:' . $id, $this->userId);
1767
-				return $share;
1768
-			}
1769
-		} catch (ShareNotFound $e) {
1770
-			// Do nothing, just try the other share type
1771
-		}
1772
-
1773
-		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1774
-			throw new ShareNotFound();
1775
-		}
1776
-		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->userId);
1777
-
1778
-		return $share;
1779
-	}
1780
-
1781
-	/**
1782
-	 * Lock a Node
1783
-	 *
1784
-	 * @param Node $node
1785
-	 * @throws LockedException
1786
-	 */
1787
-	private function lock(Node $node) {
1788
-		$node->lock(ILockingProvider::LOCK_SHARED);
1789
-		$this->lockedNode = $node;
1790
-	}
1791
-
1792
-	/**
1793
-	 * Cleanup the remaining locks
1794
-	 * @throws LockedException
1795
-	 */
1796
-	public function cleanup() {
1797
-		if ($this->lockedNode !== null) {
1798
-			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1799
-		}
1800
-	}
1801
-
1802
-	/**
1803
-	 * Returns the helper of ShareAPIController for room shares.
1804
-	 *
1805
-	 * If the Talk application is not enabled or the helper is not available
1806
-	 * a ContainerExceptionInterface is thrown instead.
1807
-	 *
1808
-	 * @return \OCA\Talk\Share\Helper\ShareAPIController
1809
-	 * @throws ContainerExceptionInterface
1810
-	 */
1811
-	private function getRoomShareHelper() {
1812
-		if (!$this->appManager->isEnabledForUser('spreed')) {
1813
-			throw new QueryException();
1814
-		}
1815
-
1816
-		return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
1817
-	}
1818
-
1819
-	/**
1820
-	 * Returns the helper of ShareAPIHelper for deck shares.
1821
-	 *
1822
-	 * If the Deck application is not enabled or the helper is not available
1823
-	 * a ContainerExceptionInterface is thrown instead.
1824
-	 *
1825
-	 * @return ShareAPIHelper
1826
-	 * @throws ContainerExceptionInterface
1827
-	 */
1828
-	private function getDeckShareHelper() {
1829
-		if (!$this->appManager->isEnabledForUser('deck')) {
1830
-			throw new QueryException();
1831
-		}
1832
-
1833
-		return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
1834
-	}
1835
-
1836
-	/**
1837
-	 * Returns the helper of ShareAPIHelper for sciencemesh shares.
1838
-	 *
1839
-	 * If the sciencemesh application is not enabled or the helper is not available
1840
-	 * a ContainerExceptionInterface is thrown instead.
1841
-	 *
1842
-	 * @return ShareAPIHelper
1843
-	 * @throws ContainerExceptionInterface
1844
-	 */
1845
-	private function getSciencemeshShareHelper() {
1846
-		if (!$this->appManager->isEnabledForUser('sciencemesh')) {
1847
-			throw new QueryException();
1848
-		}
1849
-
1850
-		return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
1851
-	}
1852
-
1853
-	/**
1854
-	 * @param string $viewer
1855
-	 * @param Node $node
1856
-	 * @param bool $reShares
1857
-	 *
1858
-	 * @return IShare[]
1859
-	 */
1860
-	private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1861
-		$providers = [
1862
-			IShare::TYPE_USER,
1863
-			IShare::TYPE_GROUP,
1864
-			IShare::TYPE_LINK,
1865
-			IShare::TYPE_EMAIL,
1866
-			IShare::TYPE_CIRCLE,
1867
-			IShare::TYPE_ROOM,
1868
-			IShare::TYPE_DECK,
1869
-			IShare::TYPE_SCIENCEMESH
1870
-		];
1871
-
1872
-		// Should we assume that the (currentUser) viewer is the owner of the node !?
1873
-		$shares = [];
1874
-		foreach ($providers as $provider) {
1875
-			if (!$this->shareManager->shareProviderExists($provider)) {
1876
-				continue;
1877
-			}
1878
-
1879
-			$providerShares
1880
-				= $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1881
-			$shares = array_merge($shares, $providerShares);
1882
-		}
1883
-
1884
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1885
-			$federatedShares = $this->shareManager->getSharesBy(
1886
-				$this->userId, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1887
-			);
1888
-			$shares = array_merge($shares, $federatedShares);
1889
-		}
1890
-
1891
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1892
-			$federatedShares = $this->shareManager->getSharesBy(
1893
-				$this->userId, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1894
-			);
1895
-			$shares = array_merge($shares, $federatedShares);
1896
-		}
1897
-
1898
-		return $shares;
1899
-	}
1900
-
1901
-
1902
-	/**
1903
-	 * @param Node $node
1904
-	 *
1905
-	 * @throws SharingRightsException
1906
-	 */
1907
-	private function confirmSharingRights(Node $node): void {
1908
-		if (!$this->hasResharingRights($this->userId, $node)) {
1909
-			throw new SharingRightsException($this->l->t('No sharing rights on this item'));
1910
-		}
1911
-	}
1912
-
1913
-
1914
-	/**
1915
-	 * @param string $viewer
1916
-	 * @param Node $node
1917
-	 *
1918
-	 * @return bool
1919
-	 */
1920
-	private function hasResharingRights($viewer, $node): bool {
1921
-		if ($viewer === $node->getOwner()->getUID()) {
1922
-			return true;
1923
-		}
1924
-
1925
-		foreach ([$node, $node->getParent()] as $node) {
1926
-			$shares = $this->getSharesFromNode($viewer, $node, true);
1927
-			foreach ($shares as $share) {
1928
-				try {
1929
-					if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1930
-						return true;
1931
-					}
1932
-				} catch (InvalidPathException|NotFoundException $e) {
1933
-				}
1934
-			}
1935
-		}
1936
-
1937
-		return false;
1938
-	}
1939
-
1940
-
1941
-	/**
1942
-	 * Returns if we can find resharing rights in an IShare object for a specific user.
1943
-	 *
1944
-	 * @suppress PhanUndeclaredClassMethod
1945
-	 *
1946
-	 * @param string $userId
1947
-	 * @param IShare $share
1948
-	 * @param Node $node
1949
-	 *
1950
-	 * @return bool
1951
-	 * @throws NotFoundException
1952
-	 * @throws InvalidPathException
1953
-	 */
1954
-	private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1955
-		if ($share->getShareOwner() === $userId) {
1956
-			return true;
1957
-		}
1958
-
1959
-		// we check that current user have parent resharing rights on the current file
1960
-		if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1961
-			return true;
1962
-		}
1963
-
1964
-		if ((Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1965
-			return false;
1966
-		}
1967
-
1968
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1969
-			return true;
1970
-		}
1971
-
1972
-		if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1973
-			return true;
1974
-		}
1975
-
1976
-		if ($share->getShareType() === IShare::TYPE_CIRCLE && Server::get(IAppManager::class)->isEnabledForUser('circles')
1977
-			&& class_exists('\OCA\Circles\Api\v1\Circles')) {
1978
-			$hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
1979
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1980
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1981
-			if ($shareWithLength === false) {
1982
-				$sharedWith = substr($share->getSharedWith(), $shareWithStart);
1983
-			} else {
1984
-				$sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1985
-			}
1986
-			try {
1987
-				$member = Circles::getMember($sharedWith, $userId, 1);
1988
-				if ($member->getLevel() >= 4) {
1989
-					return true;
1990
-				}
1991
-				return false;
1992
-			} catch (ContainerExceptionInterface $e) {
1993
-				return false;
1994
-			}
1995
-		}
1996
-
1997
-		return false;
1998
-	}
1999
-
2000
-	/**
2001
-	 * Get all the shares for the current user
2002
-	 *
2003
-	 * @param Node|null $path
2004
-	 * @param boolean $reshares
2005
-	 * @return IShare[]
2006
-	 */
2007
-	private function getAllShares(?Node $path = null, bool $reshares = false) {
2008
-		// Get all shares
2009
-		$userShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_USER, $path, $reshares, -1, 0);
2010
-		$groupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
2011
-		$linkShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_LINK, $path, $reshares, -1, 0);
2012
-
2013
-		// EMAIL SHARES
2014
-		$mailShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
2015
-
2016
-		// TEAM SHARES
2017
-		$circleShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
2018
-
2019
-		// TALK SHARES
2020
-		$roomShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
2021
-
2022
-		// DECK SHARES
2023
-		$deckShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_DECK, $path, $reshares, -1, 0);
2024
-
2025
-		// SCIENCEMESH SHARES
2026
-		$sciencemeshShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_SCIENCEMESH, $path, $reshares, -1, 0);
2027
-
2028
-		// FEDERATION
2029
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
2030
-			$federatedShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
2031
-		} else {
2032
-			$federatedShares = [];
2033
-		}
2034
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
2035
-			$federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
2036
-		} else {
2037
-			$federatedGroupShares = [];
2038
-		}
2039
-
2040
-		return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $federatedShares, $federatedGroupShares);
2041
-	}
2042
-
2043
-
2044
-	/**
2045
-	 * merging already formatted shares.
2046
-	 * We'll make an associative array to easily detect duplicate Ids.
2047
-	 * Keys _needs_ to be removed after all shares are retrieved and merged.
2048
-	 *
2049
-	 * @param array $shares
2050
-	 * @param array $newShares
2051
-	 */
2052
-	private function mergeFormattedShares(array &$shares, array $newShares) {
2053
-		foreach ($newShares as $newShare) {
2054
-			if (!array_key_exists($newShare['id'], $shares)) {
2055
-				$shares[$newShare['id']] = $newShare;
2056
-			}
2057
-		}
2058
-	}
2059
-
2060
-	/**
2061
-	 * @param IShare $share
2062
-	 * @param string|null $attributesString
2063
-	 * @return IShare modified share
2064
-	 */
2065
-	private function setShareAttributes(IShare $share, ?string $attributesString) {
2066
-		$newShareAttributes = null;
2067
-		if ($attributesString !== null) {
2068
-			$newShareAttributes = $this->shareManager->newShare()->newAttributes();
2069
-			$formattedShareAttributes = \json_decode($attributesString, true);
2070
-			if (is_array($formattedShareAttributes)) {
2071
-				foreach ($formattedShareAttributes as $formattedAttr) {
2072
-					$newShareAttributes->setAttribute(
2073
-						$formattedAttr['scope'],
2074
-						$formattedAttr['key'],
2075
-						$formattedAttr['value'],
2076
-					);
2077
-				}
2078
-			} else {
2079
-				throw new OCSBadRequestException($this->l->t('Invalid share attributes provided: "%s"', [$attributesString]));
2080
-			}
2081
-		}
2082
-		$share->setAttributes($newShareAttributes);
2083
-
2084
-		return $share;
2085
-	}
2086
-
2087
-	private function checkInheritedAttributes(IShare $share): void {
2088
-		if (!$share->getSharedBy()) {
2089
-			return; // Probably in a test
2090
-		}
2091
-
2092
-		$canDownload = false;
2093
-		$hideDownload = true;
2094
-
2095
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
2096
-		$nodes = $userFolder->getById($share->getNodeId());
2097
-		foreach ($nodes as $node) {
2098
-			// Owner always can download it - so allow it and break
2099
-			if ($node->getOwner()?->getUID() === $share->getSharedBy()) {
2100
-				$canDownload = true;
2101
-				$hideDownload = false;
2102
-				break;
2103
-			}
2104
-
2105
-			if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
2106
-				$storage = $node->getStorage();
2107
-				if ($storage instanceof Wrapper) {
2108
-					$storage = $storage->getInstanceOfStorage(SharedStorage::class);
2109
-					if ($storage === null) {
2110
-						throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
2111
-					}
2112
-				} else {
2113
-					throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
2114
-				}
2115
-
2116
-				/** @var SharedStorage $storage */
2117
-				$originalShare = $storage->getShare();
2118
-				$inheritedAttributes = $originalShare->getAttributes();
2119
-				// hide if hidden and also the current share enforces hide (can only be false if one share is false or user is owner)
2120
-				$hideDownload = $hideDownload && $originalShare->getHideDownload();
2121
-				// allow download if already allowed by previous share or when the current share allows downloading
2122
-				$canDownload = $canDownload || $inheritedAttributes === null || $inheritedAttributes->getAttribute('permissions', 'download') !== false;
2123
-			} elseif ($node->getStorage()->instanceOfStorage(Storage::class)) {
2124
-				$canDownload = true; // in case of federation storage, we can expect the download to be activated by default
2125
-			}
2126
-		}
2127
-
2128
-		if ($hideDownload || !$canDownload) {
2129
-			$share->setHideDownload(true);
2130
-
2131
-			if (!$canDownload) {
2132
-				$attributes = $share->getAttributes() ?? $share->newAttributes();
2133
-				$attributes->setAttribute('permissions', 'download', false);
2134
-				$share->setAttributes($attributes);
2135
-			}
2136
-		}
2137
-	}
2138
-
2139
-	/**
2140
-	 * Send a mail notification again for a share.
2141
-	 * The mail_send option must be enabled for the given share.
2142
-	 * @param string $id the share ID
2143
-	 * @param string $password the password to check against. Necessary for password protected shares.
2144
-	 * @throws OCSNotFoundException Share not found
2145
-	 * @throws OCSForbiddenException You are not allowed to send mail notifications
2146
-	 * @throws OCSBadRequestException Invalid request or wrong password
2147
-	 * @throws OCSException Error while sending mail notification
2148
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
2149
-	 *
2150
-	 * 200: The email notification was sent successfully
2151
-	 */
2152
-	#[NoAdminRequired]
2153
-	#[UserRateLimit(limit: 10, period: 600)]
2154
-	public function sendShareEmail(string $id, $password = ''): DataResponse {
2155
-		try {
2156
-			$share = $this->getShareById($id);
2157
-
2158
-			if (!$this->canAccessShare($share, false)) {
2159
-				throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2160
-			}
2161
-
2162
-			if (!$this->canEditShare($share)) {
2163
-				throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2164
-			}
2165
-
2166
-			// For mail and link shares, the user must be
2167
-			// the owner of the share, not only the file owner.
2168
-			if ($share->getShareType() === IShare::TYPE_EMAIL
2169
-				|| $share->getShareType() === IShare::TYPE_LINK) {
2170
-				if ($share->getSharedBy() !== $this->userId) {
2171
-					throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2172
-				}
2173
-			}
2174
-
2175
-			try {
2176
-				$provider = $this->factory->getProviderForType($share->getShareType());
2177
-				if (!($provider instanceof IShareProviderWithNotification)) {
2178
-					throw new OCSBadRequestException($this->l->t('No mail notification configured for this share type'));
2179
-				}
2180
-
2181
-				// Circumvent the password encrypted data by
2182
-				// setting the password clear. We're not storing
2183
-				// the password clear, it is just a temporary
2184
-				// object manipulation. The password will stay
2185
-				// encrypted in the database.
2186
-				if ($share->getPassword() !== null && $share->getPassword() !== $password) {
2187
-					if (!$this->shareManager->checkPassword($share, $password)) {
2188
-						throw new OCSBadRequestException($this->l->t('Wrong password'));
2189
-					}
2190
-					$share = $share->setPassword($password);
2191
-				}
2192
-
2193
-				$provider->sendMailNotification($share);
2194
-				return new DataResponse();
2195
-			} catch (Exception $e) {
2196
-				$this->logger->error($e->getMessage(), ['exception' => $e]);
2197
-				throw new OCSException($this->l->t('Error while sending mail notification'));
2198
-			}
2199
-
2200
-		} catch (ShareNotFound $e) {
2201
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2202
-		}
2203
-	}
2204
-
2205
-	/**
2206
-	 * Get a unique share token
2207
-	 *
2208
-	 * @throws OCSException Failed to generate a unique token
2209
-	 *
2210
-	 * @return DataResponse<Http::STATUS_OK, array{token: string}, array{}>
2211
-	 *
2212
-	 * 200: Token generated successfully
2213
-	 */
2214
-	#[ApiRoute(verb: 'GET', url: '/api/v1/token')]
2215
-	#[NoAdminRequired]
2216
-	public function generateToken(): DataResponse {
2217
-		try {
2218
-			$token = $this->shareManager->generateToken();
2219
-			return new DataResponse([
2220
-				'token' => $token,
2221
-			]);
2222
-		} catch (ShareTokenException $e) {
2223
-			throw new OCSException($this->l->t('Failed to generate a unique token'));
2224
-		}
2225
-	}
2226
-
2227
-	/**
2228
-	 * Populate the result set with file tags
2229
-	 *
2230
-	 * @psalm-template T of array{tags?: list<string>, file_source: int, ...array<string, mixed>}
2231
-	 * @param list<T> $fileList
2232
-	 * @return list<T> file list populated with tags
2233
-	 */
2234
-	private function populateTags(array $fileList): array {
2235
-		$tagger = $this->tagManager->load('files');
2236
-		$tags = $tagger->getTagsForObjects(array_map(static fn (array $fileData) => $fileData['file_source'], $fileList));
2237
-
2238
-		if (!is_array($tags)) {
2239
-			throw new \UnexpectedValueException('$tags must be an array');
2240
-		}
2241
-
2242
-		// Set empty tag array
2243
-		foreach ($fileList as &$fileData) {
2244
-			$fileData['tags'] = [];
2245
-		}
2246
-		unset($fileData);
2247
-
2248
-		if (!empty($tags)) {
2249
-			foreach ($tags as $fileId => $fileTags) {
2250
-				foreach ($fileList as &$fileData) {
2251
-					if ($fileId !== $fileData['file_source']) {
2252
-						continue;
2253
-					}
2254
-
2255
-					$fileData['tags'] = $fileTags;
2256
-				}
2257
-				unset($fileData);
2258
-			}
2259
-		}
2260
-
2261
-		return $fileList;
2262
-	}
1068
+                continue;
1069
+            }
1070
+
1071
+            if (in_array($share->getId(), $known)
1072
+                || ($share->getSharedWith() === $this->userId && $share->getShareType() === IShare::TYPE_USER)) {
1073
+                continue;
1074
+            }
1075
+
1076
+            $known[] = $share->getId();
1077
+            try {
1078
+                /** @var IShare $share */
1079
+                $format = $this->formatShare($share, $node);
1080
+                $formatted[] = $format;
1081
+
1082
+                // let's also build a list of shares created
1083
+                // by the current user only, in case
1084
+                // there is no resharing rights
1085
+                if ($share->getSharedBy() === $this->userId) {
1086
+                    $miniFormatted[] = $format;
1087
+                }
1088
+
1089
+                // check if one of those share is shared with me
1090
+                // and if I have resharing rights on it
1091
+                if (!$resharingRight && $this->shareProviderResharingRights($this->userId, $share, $node)) {
1092
+                    $resharingRight = true;
1093
+                }
1094
+            } catch (InvalidPathException|NotFoundException $e) {
1095
+            }
1096
+        }
1097
+
1098
+        if (!$resharingRight) {
1099
+            $formatted = $miniFormatted;
1100
+        }
1101
+
1102
+        // fix eventual missing display name from federated shares
1103
+        $formatted = $this->fixMissingDisplayName($formatted);
1104
+
1105
+        if ($includeTags) {
1106
+            $formatted = $this->populateTags($formatted);
1107
+        }
1108
+
1109
+        return $formatted;
1110
+    }
1111
+
1112
+
1113
+    /**
1114
+     * Get all shares relative to a file, including parent folders shares rights
1115
+     *
1116
+     * @param string $path Path all shares will be relative to
1117
+     *
1118
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1119
+     * @throws InvalidPathException
1120
+     * @throws NotFoundException
1121
+     * @throws OCSNotFoundException The given path is invalid
1122
+     * @throws SharingRightsException
1123
+     *
1124
+     * 200: Shares returned
1125
+     */
1126
+    #[NoAdminRequired]
1127
+    public function getInheritedShares(string $path): DataResponse {
1128
+        // get Node from (string) path.
1129
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1130
+        try {
1131
+            $node = $userFolder->get($path);
1132
+            $this->lock($node);
1133
+        } catch (NotFoundException $e) {
1134
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder does not exist'));
1135
+        } catch (LockedException $e) {
1136
+            throw new OCSNotFoundException($this->l->t('Could not lock path'));
1137
+        }
1138
+
1139
+        if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
1140
+            throw new SharingRightsException($this->l->t('no sharing rights on this item'));
1141
+        }
1142
+
1143
+        // The current top parent we have access to
1144
+        $parent = $node;
1145
+
1146
+        // initiate real owner.
1147
+        $owner = $node->getOwner()
1148
+            ->getUID();
1149
+        if (!$this->userManager->userExists($owner)) {
1150
+            return new DataResponse([]);
1151
+        }
1152
+
1153
+        // get node based on the owner, fix owner in case of external storage
1154
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1155
+        if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
1156
+            $owner = $node->getOwner()
1157
+                ->getUID();
1158
+            $userFolder = $this->rootFolder->getUserFolder($owner);
1159
+            $node = $userFolder->getFirstNodeById($node->getId());
1160
+        }
1161
+        $basePath = $userFolder->getPath();
1162
+
1163
+        // generate node list for each parent folders
1164
+        /** @var Node[] $nodes */
1165
+        $nodes = [];
1166
+        while (true) {
1167
+            $node = $node->getParent();
1168
+            if ($node->getPath() === $basePath) {
1169
+                break;
1170
+            }
1171
+            $nodes[] = $node;
1172
+        }
1173
+
1174
+        // The user that is requesting this list
1175
+        $currentUserFolder = $this->rootFolder->getUserFolder($this->userId);
1176
+
1177
+        // for each nodes, retrieve shares.
1178
+        $shares = [];
1179
+
1180
+        foreach ($nodes as $node) {
1181
+            $getShares = $this->getFormattedShares($owner, $node, false, true);
1182
+
1183
+            $currentUserNode = $currentUserFolder->getFirstNodeById($node->getId());
1184
+            if ($currentUserNode) {
1185
+                $parent = $currentUserNode;
1186
+            }
1187
+
1188
+            $subPath = $currentUserFolder->getRelativePath($parent->getPath());
1189
+            foreach ($getShares as &$share) {
1190
+                $share['via_fileid'] = $parent->getId();
1191
+                $share['via_path'] = $subPath;
1192
+            }
1193
+            $this->mergeFormattedShares($shares, $getShares);
1194
+        }
1195
+
1196
+        return new DataResponse(array_values($shares));
1197
+    }
1198
+
1199
+    /**
1200
+     * Check whether a set of permissions contains the permissions to check.
1201
+     */
1202
+    private function hasPermission(int $permissionsSet, int $permissionsToCheck): bool {
1203
+        return ($permissionsSet & $permissionsToCheck) === $permissionsToCheck;
1204
+    }
1205
+
1206
+    /**
1207
+     * Update a share
1208
+     *
1209
+     * @param string $id ID of the share
1210
+     * @param int|null $permissions New permissions
1211
+     * @param string|null $password New password
1212
+     * @param string|null $sendPasswordByTalk New condition if the password should be send over Talk
1213
+     * @param string|null $publicUpload New condition if public uploading is allowed
1214
+     * @param string|null $expireDate New expiry date
1215
+     * @param string|null $note New note
1216
+     * @param string|null $label New label
1217
+     * @param string|null $hideDownload New condition if the download should be hidden
1218
+     * @param string|null $attributes New additional attributes
1219
+     * @param string|null $sendMail if the share should be send by mail.
1220
+     *                              Considering the share already exists, no mail will be send after the share is updated.
1221
+     *                              You will have to use the sendMail action to send the mail.
1222
+     * @param string|null $shareWith New recipient for email shares
1223
+     * @param string|null $token New token
1224
+     * @return DataResponse<Http::STATUS_OK, Files_SharingShare, array{}>
1225
+     * @throws OCSBadRequestException Share could not be updated because the requested changes are invalid
1226
+     * @throws OCSForbiddenException Missing permissions to update the share
1227
+     * @throws OCSNotFoundException Share not found
1228
+     *
1229
+     * 200: Share updated successfully
1230
+     */
1231
+    #[NoAdminRequired]
1232
+    public function updateShare(
1233
+        string $id,
1234
+        ?int $permissions = null,
1235
+        ?string $password = null,
1236
+        ?string $sendPasswordByTalk = null,
1237
+        ?string $publicUpload = null,
1238
+        ?string $expireDate = null,
1239
+        ?string $note = null,
1240
+        ?string $label = null,
1241
+        ?string $hideDownload = null,
1242
+        ?string $attributes = null,
1243
+        ?string $sendMail = null,
1244
+        ?string $token = null,
1245
+    ): DataResponse {
1246
+        try {
1247
+            $share = $this->getShareById($id);
1248
+        } catch (ShareNotFound $e) {
1249
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1250
+        }
1251
+
1252
+        $this->lock($share->getNode());
1253
+
1254
+        if (!$this->canAccessShare($share, false)) {
1255
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1256
+        }
1257
+
1258
+        if (!$this->canEditShare($share)) {
1259
+            throw new OCSForbiddenException($this->l->t('You are not allowed to edit incoming shares'));
1260
+        }
1261
+
1262
+        if (
1263
+            $permissions === null
1264
+            && $password === null
1265
+            && $sendPasswordByTalk === null
1266
+            && $publicUpload === null
1267
+            && $expireDate === null
1268
+            && $note === null
1269
+            && $label === null
1270
+            && $hideDownload === null
1271
+            && $attributes === null
1272
+            && $sendMail === null
1273
+            && $token === null
1274
+        ) {
1275
+            throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
1276
+        }
1277
+
1278
+        if ($note !== null) {
1279
+            $share->setNote($note);
1280
+        }
1281
+
1282
+        if ($attributes !== null) {
1283
+            $share = $this->setShareAttributes($share, $attributes);
1284
+        }
1285
+
1286
+        // Handle mail send
1287
+        if ($sendMail === 'true' || $sendMail === 'false') {
1288
+            $share->setMailSend($sendMail === 'true');
1289
+        }
1290
+
1291
+        /**
1292
+         * expiration date, password and publicUpload only make sense for link shares
1293
+         */
1294
+        if ($share->getShareType() === IShare::TYPE_LINK
1295
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1296
+
1297
+            // Update hide download state
1298
+            if ($hideDownload === 'true') {
1299
+                $share->setHideDownload(true);
1300
+            } elseif ($hideDownload === 'false') {
1301
+                $share->setHideDownload(false);
1302
+            }
1303
+
1304
+            // If either manual permissions are specified or publicUpload
1305
+            // then we need to also update the permissions of the share
1306
+            if ($permissions !== null || $publicUpload !== null) {
1307
+                $hasPublicUpload = $this->getLegacyPublicUpload($publicUpload);
1308
+                $permissions = $this->getLinkSharePermissions($permissions ?? Constants::PERMISSION_READ, $hasPublicUpload);
1309
+                $this->validateLinkSharePermissions($share->getNode(), $permissions, $hasPublicUpload);
1310
+                $share->setPermissions($permissions);
1311
+            }
1312
+
1313
+            if ($password === '') {
1314
+                $share->setPassword(null);
1315
+            } elseif ($password !== null) {
1316
+                $share->setPassword($password);
1317
+            }
1318
+
1319
+            if ($label !== null) {
1320
+                if (strlen($label) > 255) {
1321
+                    throw new OCSBadRequestException('Maximum label length is 255');
1322
+                }
1323
+                $share->setLabel($label);
1324
+            }
1325
+
1326
+            if ($sendPasswordByTalk === 'true') {
1327
+                if (!$this->appManager->isEnabledForUser('spreed')) {
1328
+                    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.'));
1329
+                }
1330
+
1331
+                $share->setSendPasswordByTalk(true);
1332
+            } elseif ($sendPasswordByTalk !== null) {
1333
+                $share->setSendPasswordByTalk(false);
1334
+            }
1335
+
1336
+            if ($token !== null) {
1337
+                if (!$this->shareManager->allowCustomTokens()) {
1338
+                    throw new OCSForbiddenException($this->l->t('Custom share link tokens have been disabled by the administrator'));
1339
+                }
1340
+                if (!$this->validateToken($token)) {
1341
+                    throw new OCSBadRequestException($this->l->t('Tokens must contain at least 1 character and may only contain letters, numbers, or a hyphen'));
1342
+                }
1343
+                $share->setToken($token);
1344
+            }
1345
+        }
1346
+
1347
+        // NOT A LINK SHARE
1348
+        else {
1349
+            if ($permissions !== null) {
1350
+                $share->setPermissions($permissions);
1351
+            }
1352
+        }
1353
+
1354
+        if ($expireDate === '') {
1355
+            $share->setExpirationDate(null);
1356
+        } elseif ($expireDate !== null) {
1357
+            try {
1358
+                $expireDateTime = $this->parseDate($expireDate);
1359
+                $share->setExpirationDate($expireDateTime);
1360
+            } catch (\Exception $e) {
1361
+                throw new OCSBadRequestException($e->getMessage(), $e);
1362
+            }
1363
+        }
1364
+
1365
+        try {
1366
+            $this->checkInheritedAttributes($share);
1367
+            $share = $this->shareManager->updateShare($share);
1368
+        } catch (HintException $e) {
1369
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1370
+            throw new OCSException($e->getHint(), (int)$code);
1371
+        } catch (\Exception $e) {
1372
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
1373
+            throw new OCSBadRequestException('Failed to update share.', $e);
1374
+        }
1375
+
1376
+        return new DataResponse($this->formatShare($share));
1377
+    }
1378
+
1379
+    private function validateToken(string $token): bool {
1380
+        if (mb_strlen($token) === 0) {
1381
+            return false;
1382
+        }
1383
+        if (!preg_match('/^[a-z0-9-]+$/i', $token)) {
1384
+            return false;
1385
+        }
1386
+        return true;
1387
+    }
1388
+
1389
+    /**
1390
+     * Get all shares that are still pending
1391
+     *
1392
+     * @return DataResponse<Http::STATUS_OK, list<Files_SharingShare>, array{}>
1393
+     *
1394
+     * 200: Pending shares returned
1395
+     */
1396
+    #[NoAdminRequired]
1397
+    public function pendingShares(): DataResponse {
1398
+        $pendingShares = [];
1399
+
1400
+        $shareTypes = [
1401
+            IShare::TYPE_USER,
1402
+            IShare::TYPE_GROUP
1403
+        ];
1404
+
1405
+        foreach ($shareTypes as $shareType) {
1406
+            $shares = $this->shareManager->getSharedWith($this->userId, $shareType, null, -1, 0);
1407
+
1408
+            foreach ($shares as $share) {
1409
+                if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1410
+                    $pendingShares[] = $share;
1411
+                }
1412
+            }
1413
+        }
1414
+
1415
+        $result = array_values(array_filter(array_map(function (IShare $share) {
1416
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1417
+            $node = $userFolder->getFirstNodeById($share->getNodeId());
1418
+            if (!$node) {
1419
+                // fallback to guessing the path
1420
+                $node = $userFolder->get($share->getTarget());
1421
+                if ($node === null || $share->getTarget() === '') {
1422
+                    return null;
1423
+                }
1424
+            }
1425
+
1426
+            try {
1427
+                $formattedShare = $this->formatShare($share, $node);
1428
+                $formattedShare['path'] = '/' . $share->getNode()->getName();
1429
+                $formattedShare['permissions'] = 0;
1430
+                return $formattedShare;
1431
+            } catch (NotFoundException $e) {
1432
+                return null;
1433
+            }
1434
+        }, $pendingShares), function ($entry) {
1435
+            return $entry !== null;
1436
+        }));
1437
+
1438
+        return new DataResponse($result);
1439
+    }
1440
+
1441
+    /**
1442
+     * Accept a share
1443
+     *
1444
+     * @param string $id ID of the share
1445
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1446
+     * @throws OCSNotFoundException Share not found
1447
+     * @throws OCSException
1448
+     * @throws OCSBadRequestException Share could not be accepted
1449
+     *
1450
+     * 200: Share accepted successfully
1451
+     */
1452
+    #[NoAdminRequired]
1453
+    public function acceptShare(string $id): DataResponse {
1454
+        try {
1455
+            $share = $this->getShareById($id);
1456
+        } catch (ShareNotFound $e) {
1457
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1458
+        }
1459
+
1460
+        if (!$this->canAccessShare($share)) {
1461
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
1462
+        }
1463
+
1464
+        try {
1465
+            $this->shareManager->acceptShare($share, $this->userId);
1466
+        } catch (HintException $e) {
1467
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1468
+            throw new OCSException($e->getHint(), (int)$code);
1469
+        } catch (\Exception $e) {
1470
+            $this->logger->error($e->getMessage(), ['exception' => $e]);
1471
+            throw new OCSBadRequestException('Failed to accept share.', $e);
1472
+        }
1473
+
1474
+        return new DataResponse();
1475
+    }
1476
+
1477
+    /**
1478
+     * Does the user have read permission on the share
1479
+     *
1480
+     * @param IShare $share the share to check
1481
+     * @param boolean $checkGroups check groups as well?
1482
+     * @return boolean
1483
+     * @throws NotFoundException
1484
+     *
1485
+     * @suppress PhanUndeclaredClassMethod
1486
+     */
1487
+    protected function canAccessShare(IShare $share, bool $checkGroups = true): bool {
1488
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1489
+        if ($share->getPermissions() === 0) {
1490
+            return false;
1491
+        }
1492
+
1493
+        // Owner of the file and the sharer of the file can always get share
1494
+        if ($share->getShareOwner() === $this->userId
1495
+            || $share->getSharedBy() === $this->userId) {
1496
+            return true;
1497
+        }
1498
+
1499
+        // If the share is shared with you, you can access it!
1500
+        if ($share->getShareType() === IShare::TYPE_USER
1501
+            && $share->getSharedWith() === $this->userId) {
1502
+            return true;
1503
+        }
1504
+
1505
+        // Have reshare rights on the shared file/folder ?
1506
+        // Does the currentUser have access to the shared file?
1507
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1508
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1509
+        if ($file && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1510
+            return true;
1511
+        }
1512
+
1513
+        // If in the recipient group, you can see the share
1514
+        if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1515
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1516
+            $user = $this->userManager->get($this->userId);
1517
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1518
+                return true;
1519
+            }
1520
+        }
1521
+
1522
+        if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1523
+            // TODO: have a sanity check like above?
1524
+            return true;
1525
+        }
1526
+
1527
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1528
+            try {
1529
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1530
+            } catch (ContainerExceptionInterface $e) {
1531
+                return false;
1532
+            }
1533
+        }
1534
+
1535
+        if ($share->getShareType() === IShare::TYPE_DECK) {
1536
+            try {
1537
+                return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1538
+            } catch (ContainerExceptionInterface $e) {
1539
+                return false;
1540
+            }
1541
+        }
1542
+
1543
+        if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1544
+            try {
1545
+                return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1546
+            } catch (ContainerExceptionInterface $e) {
1547
+                return false;
1548
+            }
1549
+        }
1550
+
1551
+        return false;
1552
+    }
1553
+
1554
+    /**
1555
+     * Does the user have edit permission on the share
1556
+     *
1557
+     * @param IShare $share the share to check
1558
+     * @return boolean
1559
+     */
1560
+    protected function canEditShare(IShare $share): bool {
1561
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1562
+        if ($share->getPermissions() === 0) {
1563
+            return false;
1564
+        }
1565
+
1566
+        // The owner of the file and the creator of the share
1567
+        // can always edit the share
1568
+        if ($share->getShareOwner() === $this->userId
1569
+            || $share->getSharedBy() === $this->userId
1570
+        ) {
1571
+            return true;
1572
+        }
1573
+
1574
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1575
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1576
+        if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1577
+            return true;
1578
+        }
1579
+
1580
+        //! we do NOT support some kind of `admin` in groups.
1581
+        //! You cannot edit shares shared to a group you're
1582
+        //! a member of if you're not the share owner or the file owner!
1583
+
1584
+        return false;
1585
+    }
1586
+
1587
+    /**
1588
+     * Does the user have delete permission on the share
1589
+     *
1590
+     * @param IShare $share the share to check
1591
+     * @return boolean
1592
+     */
1593
+    protected function canDeleteShare(IShare $share): bool {
1594
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1595
+        if ($share->getPermissions() === 0) {
1596
+            return false;
1597
+        }
1598
+
1599
+        // if the user is the recipient, i can unshare
1600
+        // the share with self
1601
+        if ($share->getShareType() === IShare::TYPE_USER
1602
+            && $share->getSharedWith() === $this->userId
1603
+        ) {
1604
+            return true;
1605
+        }
1606
+
1607
+        // The owner of the file and the creator of the share
1608
+        // can always delete the share
1609
+        if ($share->getShareOwner() === $this->userId
1610
+            || $share->getSharedBy() === $this->userId
1611
+        ) {
1612
+            return true;
1613
+        }
1614
+
1615
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
1616
+        $file = $userFolder->getFirstNodeById($share->getNodeId());
1617
+        if ($file?->getMountPoint() instanceof IShareOwnerlessMount && $this->shareProviderResharingRights($this->userId, $share, $file)) {
1618
+            return true;
1619
+        }
1620
+
1621
+        return false;
1622
+    }
1623
+
1624
+    /**
1625
+     * Does the user have delete permission on the share
1626
+     * This differs from the canDeleteShare function as it only
1627
+     * remove the share for the current user. It does NOT
1628
+     * completely delete the share but only the mount point.
1629
+     * It can then be restored from the deleted shares section.
1630
+     *
1631
+     * @param IShare $share the share to check
1632
+     * @return boolean
1633
+     *
1634
+     * @suppress PhanUndeclaredClassMethod
1635
+     */
1636
+    protected function canDeleteShareFromSelf(IShare $share): bool {
1637
+        if ($share->getShareType() !== IShare::TYPE_GROUP
1638
+            && $share->getShareType() !== IShare::TYPE_ROOM
1639
+            && $share->getShareType() !== IShare::TYPE_DECK
1640
+            && $share->getShareType() !== IShare::TYPE_SCIENCEMESH
1641
+        ) {
1642
+            return false;
1643
+        }
1644
+
1645
+        if ($share->getShareOwner() === $this->userId
1646
+            || $share->getSharedBy() === $this->userId
1647
+        ) {
1648
+            // Delete the whole share, not just for self
1649
+            return false;
1650
+        }
1651
+
1652
+        // If in the recipient group, you can delete the share from self
1653
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1654
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1655
+            $user = $this->userManager->get($this->userId);
1656
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1657
+                return true;
1658
+            }
1659
+        }
1660
+
1661
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1662
+            try {
1663
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
1664
+            } catch (ContainerExceptionInterface $e) {
1665
+                return false;
1666
+            }
1667
+        }
1668
+
1669
+        if ($share->getShareType() === IShare::TYPE_DECK) {
1670
+            try {
1671
+                return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
1672
+            } catch (ContainerExceptionInterface $e) {
1673
+                return false;
1674
+            }
1675
+        }
1676
+
1677
+        if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
1678
+            try {
1679
+                return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
1680
+            } catch (ContainerExceptionInterface $e) {
1681
+                return false;
1682
+            }
1683
+        }
1684
+
1685
+        return false;
1686
+    }
1687
+
1688
+    /**
1689
+     * Make sure that the passed date is valid ISO 8601
1690
+     * So YYYY-MM-DD
1691
+     * If not throw an exception
1692
+     *
1693
+     * @param string $expireDate
1694
+     *
1695
+     * @throws \Exception
1696
+     * @return \DateTime
1697
+     */
1698
+    private function parseDate(string $expireDate): \DateTime {
1699
+        try {
1700
+            $date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
1701
+            // Make sure it expires at midnight in owner timezone
1702
+            $date->setTime(0, 0, 0);
1703
+        } catch (\Exception $e) {
1704
+            throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
1705
+        }
1706
+
1707
+        return $date;
1708
+    }
1709
+
1710
+    /**
1711
+     * Since we have multiple providers but the OCS Share API v1 does
1712
+     * not support this we need to check all backends.
1713
+     *
1714
+     * @param string $id
1715
+     * @return IShare
1716
+     * @throws ShareNotFound
1717
+     */
1718
+    private function getShareById(string $id): IShare {
1719
+        $share = null;
1720
+
1721
+        // First check if it is an internal share.
1722
+        try {
1723
+            $share = $this->shareManager->getShareById('ocinternal:' . $id, $this->userId);
1724
+            return $share;
1725
+        } catch (ShareNotFound $e) {
1726
+            // Do nothing, just try the other share type
1727
+        }
1728
+
1729
+
1730
+        try {
1731
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_CIRCLE)) {
1732
+                $share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->userId);
1733
+                return $share;
1734
+            }
1735
+        } catch (ShareNotFound $e) {
1736
+            // Do nothing, just try the other share type
1737
+        }
1738
+
1739
+        try {
1740
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
1741
+                $share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->userId);
1742
+                return $share;
1743
+            }
1744
+        } catch (ShareNotFound $e) {
1745
+            // Do nothing, just try the other share type
1746
+        }
1747
+
1748
+        try {
1749
+            $share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->userId);
1750
+            return $share;
1751
+        } catch (ShareNotFound $e) {
1752
+            // Do nothing, just try the other share type
1753
+        }
1754
+
1755
+        try {
1756
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_DECK)) {
1757
+                $share = $this->shareManager->getShareById('deck:' . $id, $this->userId);
1758
+                return $share;
1759
+            }
1760
+        } catch (ShareNotFound $e) {
1761
+            // Do nothing, just try the other share type
1762
+        }
1763
+
1764
+        try {
1765
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_SCIENCEMESH)) {
1766
+                $share = $this->shareManager->getShareById('sciencemesh:' . $id, $this->userId);
1767
+                return $share;
1768
+            }
1769
+        } catch (ShareNotFound $e) {
1770
+            // Do nothing, just try the other share type
1771
+        }
1772
+
1773
+        if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1774
+            throw new ShareNotFound();
1775
+        }
1776
+        $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->userId);
1777
+
1778
+        return $share;
1779
+    }
1780
+
1781
+    /**
1782
+     * Lock a Node
1783
+     *
1784
+     * @param Node $node
1785
+     * @throws LockedException
1786
+     */
1787
+    private function lock(Node $node) {
1788
+        $node->lock(ILockingProvider::LOCK_SHARED);
1789
+        $this->lockedNode = $node;
1790
+    }
1791
+
1792
+    /**
1793
+     * Cleanup the remaining locks
1794
+     * @throws LockedException
1795
+     */
1796
+    public function cleanup() {
1797
+        if ($this->lockedNode !== null) {
1798
+            $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1799
+        }
1800
+    }
1801
+
1802
+    /**
1803
+     * Returns the helper of ShareAPIController for room shares.
1804
+     *
1805
+     * If the Talk application is not enabled or the helper is not available
1806
+     * a ContainerExceptionInterface is thrown instead.
1807
+     *
1808
+     * @return \OCA\Talk\Share\Helper\ShareAPIController
1809
+     * @throws ContainerExceptionInterface
1810
+     */
1811
+    private function getRoomShareHelper() {
1812
+        if (!$this->appManager->isEnabledForUser('spreed')) {
1813
+            throw new QueryException();
1814
+        }
1815
+
1816
+        return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
1817
+    }
1818
+
1819
+    /**
1820
+     * Returns the helper of ShareAPIHelper for deck shares.
1821
+     *
1822
+     * If the Deck application is not enabled or the helper is not available
1823
+     * a ContainerExceptionInterface is thrown instead.
1824
+     *
1825
+     * @return ShareAPIHelper
1826
+     * @throws ContainerExceptionInterface
1827
+     */
1828
+    private function getDeckShareHelper() {
1829
+        if (!$this->appManager->isEnabledForUser('deck')) {
1830
+            throw new QueryException();
1831
+        }
1832
+
1833
+        return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
1834
+    }
1835
+
1836
+    /**
1837
+     * Returns the helper of ShareAPIHelper for sciencemesh shares.
1838
+     *
1839
+     * If the sciencemesh application is not enabled or the helper is not available
1840
+     * a ContainerExceptionInterface is thrown instead.
1841
+     *
1842
+     * @return ShareAPIHelper
1843
+     * @throws ContainerExceptionInterface
1844
+     */
1845
+    private function getSciencemeshShareHelper() {
1846
+        if (!$this->appManager->isEnabledForUser('sciencemesh')) {
1847
+            throw new QueryException();
1848
+        }
1849
+
1850
+        return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
1851
+    }
1852
+
1853
+    /**
1854
+     * @param string $viewer
1855
+     * @param Node $node
1856
+     * @param bool $reShares
1857
+     *
1858
+     * @return IShare[]
1859
+     */
1860
+    private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1861
+        $providers = [
1862
+            IShare::TYPE_USER,
1863
+            IShare::TYPE_GROUP,
1864
+            IShare::TYPE_LINK,
1865
+            IShare::TYPE_EMAIL,
1866
+            IShare::TYPE_CIRCLE,
1867
+            IShare::TYPE_ROOM,
1868
+            IShare::TYPE_DECK,
1869
+            IShare::TYPE_SCIENCEMESH
1870
+        ];
1871
+
1872
+        // Should we assume that the (currentUser) viewer is the owner of the node !?
1873
+        $shares = [];
1874
+        foreach ($providers as $provider) {
1875
+            if (!$this->shareManager->shareProviderExists($provider)) {
1876
+                continue;
1877
+            }
1878
+
1879
+            $providerShares
1880
+                = $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1881
+            $shares = array_merge($shares, $providerShares);
1882
+        }
1883
+
1884
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1885
+            $federatedShares = $this->shareManager->getSharesBy(
1886
+                $this->userId, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1887
+            );
1888
+            $shares = array_merge($shares, $federatedShares);
1889
+        }
1890
+
1891
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1892
+            $federatedShares = $this->shareManager->getSharesBy(
1893
+                $this->userId, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1894
+            );
1895
+            $shares = array_merge($shares, $federatedShares);
1896
+        }
1897
+
1898
+        return $shares;
1899
+    }
1900
+
1901
+
1902
+    /**
1903
+     * @param Node $node
1904
+     *
1905
+     * @throws SharingRightsException
1906
+     */
1907
+    private function confirmSharingRights(Node $node): void {
1908
+        if (!$this->hasResharingRights($this->userId, $node)) {
1909
+            throw new SharingRightsException($this->l->t('No sharing rights on this item'));
1910
+        }
1911
+    }
1912
+
1913
+
1914
+    /**
1915
+     * @param string $viewer
1916
+     * @param Node $node
1917
+     *
1918
+     * @return bool
1919
+     */
1920
+    private function hasResharingRights($viewer, $node): bool {
1921
+        if ($viewer === $node->getOwner()->getUID()) {
1922
+            return true;
1923
+        }
1924
+
1925
+        foreach ([$node, $node->getParent()] as $node) {
1926
+            $shares = $this->getSharesFromNode($viewer, $node, true);
1927
+            foreach ($shares as $share) {
1928
+                try {
1929
+                    if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1930
+                        return true;
1931
+                    }
1932
+                } catch (InvalidPathException|NotFoundException $e) {
1933
+                }
1934
+            }
1935
+        }
1936
+
1937
+        return false;
1938
+    }
1939
+
1940
+
1941
+    /**
1942
+     * Returns if we can find resharing rights in an IShare object for a specific user.
1943
+     *
1944
+     * @suppress PhanUndeclaredClassMethod
1945
+     *
1946
+     * @param string $userId
1947
+     * @param IShare $share
1948
+     * @param Node $node
1949
+     *
1950
+     * @return bool
1951
+     * @throws NotFoundException
1952
+     * @throws InvalidPathException
1953
+     */
1954
+    private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1955
+        if ($share->getShareOwner() === $userId) {
1956
+            return true;
1957
+        }
1958
+
1959
+        // we check that current user have parent resharing rights on the current file
1960
+        if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1961
+            return true;
1962
+        }
1963
+
1964
+        if ((Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1965
+            return false;
1966
+        }
1967
+
1968
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1969
+            return true;
1970
+        }
1971
+
1972
+        if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1973
+            return true;
1974
+        }
1975
+
1976
+        if ($share->getShareType() === IShare::TYPE_CIRCLE && Server::get(IAppManager::class)->isEnabledForUser('circles')
1977
+            && class_exists('\OCA\Circles\Api\v1\Circles')) {
1978
+            $hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
1979
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1980
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1981
+            if ($shareWithLength === false) {
1982
+                $sharedWith = substr($share->getSharedWith(), $shareWithStart);
1983
+            } else {
1984
+                $sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1985
+            }
1986
+            try {
1987
+                $member = Circles::getMember($sharedWith, $userId, 1);
1988
+                if ($member->getLevel() >= 4) {
1989
+                    return true;
1990
+                }
1991
+                return false;
1992
+            } catch (ContainerExceptionInterface $e) {
1993
+                return false;
1994
+            }
1995
+        }
1996
+
1997
+        return false;
1998
+    }
1999
+
2000
+    /**
2001
+     * Get all the shares for the current user
2002
+     *
2003
+     * @param Node|null $path
2004
+     * @param boolean $reshares
2005
+     * @return IShare[]
2006
+     */
2007
+    private function getAllShares(?Node $path = null, bool $reshares = false) {
2008
+        // Get all shares
2009
+        $userShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_USER, $path, $reshares, -1, 0);
2010
+        $groupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
2011
+        $linkShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_LINK, $path, $reshares, -1, 0);
2012
+
2013
+        // EMAIL SHARES
2014
+        $mailShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
2015
+
2016
+        // TEAM SHARES
2017
+        $circleShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
2018
+
2019
+        // TALK SHARES
2020
+        $roomShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
2021
+
2022
+        // DECK SHARES
2023
+        $deckShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_DECK, $path, $reshares, -1, 0);
2024
+
2025
+        // SCIENCEMESH SHARES
2026
+        $sciencemeshShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_SCIENCEMESH, $path, $reshares, -1, 0);
2027
+
2028
+        // FEDERATION
2029
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
2030
+            $federatedShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
2031
+        } else {
2032
+            $federatedShares = [];
2033
+        }
2034
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
2035
+            $federatedGroupShares = $this->shareManager->getSharesBy($this->userId, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
2036
+        } else {
2037
+            $federatedGroupShares = [];
2038
+        }
2039
+
2040
+        return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $deckShares, $sciencemeshShares, $federatedShares, $federatedGroupShares);
2041
+    }
2042
+
2043
+
2044
+    /**
2045
+     * merging already formatted shares.
2046
+     * We'll make an associative array to easily detect duplicate Ids.
2047
+     * Keys _needs_ to be removed after all shares are retrieved and merged.
2048
+     *
2049
+     * @param array $shares
2050
+     * @param array $newShares
2051
+     */
2052
+    private function mergeFormattedShares(array &$shares, array $newShares) {
2053
+        foreach ($newShares as $newShare) {
2054
+            if (!array_key_exists($newShare['id'], $shares)) {
2055
+                $shares[$newShare['id']] = $newShare;
2056
+            }
2057
+        }
2058
+    }
2059
+
2060
+    /**
2061
+     * @param IShare $share
2062
+     * @param string|null $attributesString
2063
+     * @return IShare modified share
2064
+     */
2065
+    private function setShareAttributes(IShare $share, ?string $attributesString) {
2066
+        $newShareAttributes = null;
2067
+        if ($attributesString !== null) {
2068
+            $newShareAttributes = $this->shareManager->newShare()->newAttributes();
2069
+            $formattedShareAttributes = \json_decode($attributesString, true);
2070
+            if (is_array($formattedShareAttributes)) {
2071
+                foreach ($formattedShareAttributes as $formattedAttr) {
2072
+                    $newShareAttributes->setAttribute(
2073
+                        $formattedAttr['scope'],
2074
+                        $formattedAttr['key'],
2075
+                        $formattedAttr['value'],
2076
+                    );
2077
+                }
2078
+            } else {
2079
+                throw new OCSBadRequestException($this->l->t('Invalid share attributes provided: "%s"', [$attributesString]));
2080
+            }
2081
+        }
2082
+        $share->setAttributes($newShareAttributes);
2083
+
2084
+        return $share;
2085
+    }
2086
+
2087
+    private function checkInheritedAttributes(IShare $share): void {
2088
+        if (!$share->getSharedBy()) {
2089
+            return; // Probably in a test
2090
+        }
2091
+
2092
+        $canDownload = false;
2093
+        $hideDownload = true;
2094
+
2095
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
2096
+        $nodes = $userFolder->getById($share->getNodeId());
2097
+        foreach ($nodes as $node) {
2098
+            // Owner always can download it - so allow it and break
2099
+            if ($node->getOwner()?->getUID() === $share->getSharedBy()) {
2100
+                $canDownload = true;
2101
+                $hideDownload = false;
2102
+                break;
2103
+            }
2104
+
2105
+            if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
2106
+                $storage = $node->getStorage();
2107
+                if ($storage instanceof Wrapper) {
2108
+                    $storage = $storage->getInstanceOfStorage(SharedStorage::class);
2109
+                    if ($storage === null) {
2110
+                        throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
2111
+                    }
2112
+                } else {
2113
+                    throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
2114
+                }
2115
+
2116
+                /** @var SharedStorage $storage */
2117
+                $originalShare = $storage->getShare();
2118
+                $inheritedAttributes = $originalShare->getAttributes();
2119
+                // hide if hidden and also the current share enforces hide (can only be false if one share is false or user is owner)
2120
+                $hideDownload = $hideDownload && $originalShare->getHideDownload();
2121
+                // allow download if already allowed by previous share or when the current share allows downloading
2122
+                $canDownload = $canDownload || $inheritedAttributes === null || $inheritedAttributes->getAttribute('permissions', 'download') !== false;
2123
+            } elseif ($node->getStorage()->instanceOfStorage(Storage::class)) {
2124
+                $canDownload = true; // in case of federation storage, we can expect the download to be activated by default
2125
+            }
2126
+        }
2127
+
2128
+        if ($hideDownload || !$canDownload) {
2129
+            $share->setHideDownload(true);
2130
+
2131
+            if (!$canDownload) {
2132
+                $attributes = $share->getAttributes() ?? $share->newAttributes();
2133
+                $attributes->setAttribute('permissions', 'download', false);
2134
+                $share->setAttributes($attributes);
2135
+            }
2136
+        }
2137
+    }
2138
+
2139
+    /**
2140
+     * Send a mail notification again for a share.
2141
+     * The mail_send option must be enabled for the given share.
2142
+     * @param string $id the share ID
2143
+     * @param string $password the password to check against. Necessary for password protected shares.
2144
+     * @throws OCSNotFoundException Share not found
2145
+     * @throws OCSForbiddenException You are not allowed to send mail notifications
2146
+     * @throws OCSBadRequestException Invalid request or wrong password
2147
+     * @throws OCSException Error while sending mail notification
2148
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
2149
+     *
2150
+     * 200: The email notification was sent successfully
2151
+     */
2152
+    #[NoAdminRequired]
2153
+    #[UserRateLimit(limit: 10, period: 600)]
2154
+    public function sendShareEmail(string $id, $password = ''): DataResponse {
2155
+        try {
2156
+            $share = $this->getShareById($id);
2157
+
2158
+            if (!$this->canAccessShare($share, false)) {
2159
+                throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2160
+            }
2161
+
2162
+            if (!$this->canEditShare($share)) {
2163
+                throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2164
+            }
2165
+
2166
+            // For mail and link shares, the user must be
2167
+            // the owner of the share, not only the file owner.
2168
+            if ($share->getShareType() === IShare::TYPE_EMAIL
2169
+                || $share->getShareType() === IShare::TYPE_LINK) {
2170
+                if ($share->getSharedBy() !== $this->userId) {
2171
+                    throw new OCSForbiddenException($this->l->t('You are not allowed to send mail notifications'));
2172
+                }
2173
+            }
2174
+
2175
+            try {
2176
+                $provider = $this->factory->getProviderForType($share->getShareType());
2177
+                if (!($provider instanceof IShareProviderWithNotification)) {
2178
+                    throw new OCSBadRequestException($this->l->t('No mail notification configured for this share type'));
2179
+                }
2180
+
2181
+                // Circumvent the password encrypted data by
2182
+                // setting the password clear. We're not storing
2183
+                // the password clear, it is just a temporary
2184
+                // object manipulation. The password will stay
2185
+                // encrypted in the database.
2186
+                if ($share->getPassword() !== null && $share->getPassword() !== $password) {
2187
+                    if (!$this->shareManager->checkPassword($share, $password)) {
2188
+                        throw new OCSBadRequestException($this->l->t('Wrong password'));
2189
+                    }
2190
+                    $share = $share->setPassword($password);
2191
+                }
2192
+
2193
+                $provider->sendMailNotification($share);
2194
+                return new DataResponse();
2195
+            } catch (Exception $e) {
2196
+                $this->logger->error($e->getMessage(), ['exception' => $e]);
2197
+                throw new OCSException($this->l->t('Error while sending mail notification'));
2198
+            }
2199
+
2200
+        } catch (ShareNotFound $e) {
2201
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist'));
2202
+        }
2203
+    }
2204
+
2205
+    /**
2206
+     * Get a unique share token
2207
+     *
2208
+     * @throws OCSException Failed to generate a unique token
2209
+     *
2210
+     * @return DataResponse<Http::STATUS_OK, array{token: string}, array{}>
2211
+     *
2212
+     * 200: Token generated successfully
2213
+     */
2214
+    #[ApiRoute(verb: 'GET', url: '/api/v1/token')]
2215
+    #[NoAdminRequired]
2216
+    public function generateToken(): DataResponse {
2217
+        try {
2218
+            $token = $this->shareManager->generateToken();
2219
+            return new DataResponse([
2220
+                'token' => $token,
2221
+            ]);
2222
+        } catch (ShareTokenException $e) {
2223
+            throw new OCSException($this->l->t('Failed to generate a unique token'));
2224
+        }
2225
+    }
2226
+
2227
+    /**
2228
+     * Populate the result set with file tags
2229
+     *
2230
+     * @psalm-template T of array{tags?: list<string>, file_source: int, ...array<string, mixed>}
2231
+     * @param list<T> $fileList
2232
+     * @return list<T> file list populated with tags
2233
+     */
2234
+    private function populateTags(array $fileList): array {
2235
+        $tagger = $this->tagManager->load('files');
2236
+        $tags = $tagger->getTagsForObjects(array_map(static fn (array $fileData) => $fileData['file_source'], $fileList));
2237
+
2238
+        if (!is_array($tags)) {
2239
+            throw new \UnexpectedValueException('$tags must be an array');
2240
+        }
2241
+
2242
+        // Set empty tag array
2243
+        foreach ($fileList as &$fileData) {
2244
+            $fileData['tags'] = [];
2245
+        }
2246
+        unset($fileData);
2247
+
2248
+        if (!empty($tags)) {
2249
+            foreach ($tags as $fileId => $fileTags) {
2250
+                foreach ($fileList as &$fileData) {
2251
+                    if ($fileId !== $fileData['file_source']) {
2252
+                        continue;
2253
+                    }
2254
+
2255
+                    $fileData['tags'] = $fileTags;
2256
+                }
2257
+                unset($fileData);
2258
+            }
2259
+        }
2260
+
2261
+        return $fileList;
2262
+    }
2263 2263
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/External/Manager.php 1 patch
Indentation   +752 added lines, -752 removed lines patch added patch discarded remove patch
@@ -36,803 +36,803 @@
 block discarded – undo
36 36
  * @psalm-import-type Files_SharingRemoteShare from ResponseDefinitions
37 37
  */
38 38
 class Manager {
39
-	public const STORAGE = '\OCA\Files_Sharing\External\Storage';
40
-
41
-	/** @var string|null */
42
-	private $uid;
43
-
44
-	public function __construct(
45
-		private IDBConnection $connection,
46
-		private \OC\Files\Mount\Manager $mountManager,
47
-		private IStorageFactory $storageLoader,
48
-		private IClientService $clientService,
49
-		private IManager $notificationManager,
50
-		private IDiscoveryService $discoveryService,
51
-		private ICloudFederationProviderManager $cloudFederationProviderManager,
52
-		private ICloudFederationFactory $cloudFederationFactory,
53
-		private IGroupManager $groupManager,
54
-		private IUserManager $userManager,
55
-		IUserSession $userSession,
56
-		private IEventDispatcher $eventDispatcher,
57
-		private LoggerInterface $logger,
58
-	) {
59
-		$user = $userSession->getUser();
60
-		$this->uid = $user ? $user->getUID() : null;
61
-	}
62
-
63
-	/**
64
-	 * add new server-to-server share
65
-	 *
66
-	 * @param string $remote
67
-	 * @param string $token
68
-	 * @param string $password
69
-	 * @param string $name
70
-	 * @param string $owner
71
-	 * @param int $shareType
72
-	 * @param boolean $accepted
73
-	 * @param string $user
74
-	 * @param string $remoteId
75
-	 * @param int $parent
76
-	 * @return Mount|null
77
-	 * @throws \Doctrine\DBAL\Exception
78
-	 */
79
-	public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted = false, $user = null, $remoteId = '', $parent = -1) {
80
-		$user = $user ?? $this->uid;
81
-		$accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING;
82
-		$name = Filesystem::normalizePath('/' . $name);
83
-
84
-		if ($accepted !== IShare::STATUS_ACCEPTED) {
85
-			// To avoid conflicts with the mount point generation later,
86
-			// we only use a temporary mount point name here. The real
87
-			// mount point name will be generated when accepting the share,
88
-			// using the original share item name.
89
-			$tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}';
90
-			$mountPoint = $tmpMountPointName;
91
-			$hash = md5($tmpMountPointName);
92
-			$data = [
93
-				'remote' => $remote,
94
-				'share_token' => $token,
95
-				'password' => $password,
96
-				'name' => $name,
97
-				'owner' => $owner,
98
-				'user' => $user,
99
-				'mountpoint' => $mountPoint,
100
-				'mountpoint_hash' => $hash,
101
-				'accepted' => $accepted,
102
-				'remote_id' => $remoteId,
103
-				'share_type' => $shareType,
104
-			];
105
-
106
-			$i = 1;
107
-			while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) {
108
-				// The external share already exists for the user
109
-				$data['mountpoint'] = $tmpMountPointName . '-' . $i;
110
-				$data['mountpoint_hash'] = md5($data['mountpoint']);
111
-				$i++;
112
-			}
113
-			return null;
114
-		}
115
-
116
-		$mountPoint = Files::buildNotExistingFileName('/', $name);
117
-		$mountPoint = Filesystem::normalizePath('/' . $mountPoint);
118
-		$hash = md5($mountPoint);
119
-
120
-		$this->writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType);
121
-
122
-		$options = [
123
-			'remote' => $remote,
124
-			'token' => $token,
125
-			'password' => $password,
126
-			'mountpoint' => $mountPoint,
127
-			'owner' => $owner
128
-		];
129
-		return $this->mountShare($options, $user);
130
-	}
131
-
132
-	/**
133
-	 * write remote share to the database
134
-	 *
135
-	 * @param $remote
136
-	 * @param $token
137
-	 * @param $password
138
-	 * @param $name
139
-	 * @param $owner
140
-	 * @param $user
141
-	 * @param $mountPoint
142
-	 * @param $hash
143
-	 * @param $accepted
144
-	 * @param $remoteId
145
-	 * @param $parent
146
-	 * @param $shareType
147
-	 *
148
-	 * @return void
149
-	 * @throws \Doctrine\DBAL\Driver\Exception
150
-	 */
151
-	private function writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType): void {
152
-		$query = $this->connection->prepare('
39
+    public const STORAGE = '\OCA\Files_Sharing\External\Storage';
40
+
41
+    /** @var string|null */
42
+    private $uid;
43
+
44
+    public function __construct(
45
+        private IDBConnection $connection,
46
+        private \OC\Files\Mount\Manager $mountManager,
47
+        private IStorageFactory $storageLoader,
48
+        private IClientService $clientService,
49
+        private IManager $notificationManager,
50
+        private IDiscoveryService $discoveryService,
51
+        private ICloudFederationProviderManager $cloudFederationProviderManager,
52
+        private ICloudFederationFactory $cloudFederationFactory,
53
+        private IGroupManager $groupManager,
54
+        private IUserManager $userManager,
55
+        IUserSession $userSession,
56
+        private IEventDispatcher $eventDispatcher,
57
+        private LoggerInterface $logger,
58
+    ) {
59
+        $user = $userSession->getUser();
60
+        $this->uid = $user ? $user->getUID() : null;
61
+    }
62
+
63
+    /**
64
+     * add new server-to-server share
65
+     *
66
+     * @param string $remote
67
+     * @param string $token
68
+     * @param string $password
69
+     * @param string $name
70
+     * @param string $owner
71
+     * @param int $shareType
72
+     * @param boolean $accepted
73
+     * @param string $user
74
+     * @param string $remoteId
75
+     * @param int $parent
76
+     * @return Mount|null
77
+     * @throws \Doctrine\DBAL\Exception
78
+     */
79
+    public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted = false, $user = null, $remoteId = '', $parent = -1) {
80
+        $user = $user ?? $this->uid;
81
+        $accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING;
82
+        $name = Filesystem::normalizePath('/' . $name);
83
+
84
+        if ($accepted !== IShare::STATUS_ACCEPTED) {
85
+            // To avoid conflicts with the mount point generation later,
86
+            // we only use a temporary mount point name here. The real
87
+            // mount point name will be generated when accepting the share,
88
+            // using the original share item name.
89
+            $tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}';
90
+            $mountPoint = $tmpMountPointName;
91
+            $hash = md5($tmpMountPointName);
92
+            $data = [
93
+                'remote' => $remote,
94
+                'share_token' => $token,
95
+                'password' => $password,
96
+                'name' => $name,
97
+                'owner' => $owner,
98
+                'user' => $user,
99
+                'mountpoint' => $mountPoint,
100
+                'mountpoint_hash' => $hash,
101
+                'accepted' => $accepted,
102
+                'remote_id' => $remoteId,
103
+                'share_type' => $shareType,
104
+            ];
105
+
106
+            $i = 1;
107
+            while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) {
108
+                // The external share already exists for the user
109
+                $data['mountpoint'] = $tmpMountPointName . '-' . $i;
110
+                $data['mountpoint_hash'] = md5($data['mountpoint']);
111
+                $i++;
112
+            }
113
+            return null;
114
+        }
115
+
116
+        $mountPoint = Files::buildNotExistingFileName('/', $name);
117
+        $mountPoint = Filesystem::normalizePath('/' . $mountPoint);
118
+        $hash = md5($mountPoint);
119
+
120
+        $this->writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType);
121
+
122
+        $options = [
123
+            'remote' => $remote,
124
+            'token' => $token,
125
+            'password' => $password,
126
+            'mountpoint' => $mountPoint,
127
+            'owner' => $owner
128
+        ];
129
+        return $this->mountShare($options, $user);
130
+    }
131
+
132
+    /**
133
+     * write remote share to the database
134
+     *
135
+     * @param $remote
136
+     * @param $token
137
+     * @param $password
138
+     * @param $name
139
+     * @param $owner
140
+     * @param $user
141
+     * @param $mountPoint
142
+     * @param $hash
143
+     * @param $accepted
144
+     * @param $remoteId
145
+     * @param $parent
146
+     * @param $shareType
147
+     *
148
+     * @return void
149
+     * @throws \Doctrine\DBAL\Driver\Exception
150
+     */
151
+    private function writeShareToDb($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType): void {
152
+        $query = $this->connection->prepare('
153 153
 				INSERT INTO `*PREFIX*share_external`
154 154
 					(`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`, `parent`, `share_type`)
155 155
 				VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
156 156
 			');
157
-		$query->execute([$remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType]);
158
-	}
157
+        $query->execute([$remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType]);
158
+    }
159 159
 
160
-	private function fetchShare(int $id): array|false {
161
-		$getShare = $this->connection->prepare('
160
+    private function fetchShare(int $id): array|false {
161
+        $getShare = $this->connection->prepare('
162 162
 			SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
163 163
 			FROM  `*PREFIX*share_external`
164 164
 			WHERE `id` = ?');
165
-		$result = $getShare->execute([$id]);
166
-		$share = $result->fetch();
167
-		$result->closeCursor();
168
-		return $share;
169
-	}
170
-
171
-	/**
172
-	 * get share by token
173
-	 *
174
-	 * @param string $token
175
-	 * @return mixed share of false
176
-	 */
177
-	private function fetchShareByToken($token) {
178
-		$getShare = $this->connection->prepare('
165
+        $result = $getShare->execute([$id]);
166
+        $share = $result->fetch();
167
+        $result->closeCursor();
168
+        return $share;
169
+    }
170
+
171
+    /**
172
+     * get share by token
173
+     *
174
+     * @param string $token
175
+     * @return mixed share of false
176
+     */
177
+    private function fetchShareByToken($token) {
178
+        $getShare = $this->connection->prepare('
179 179
 			SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
180 180
 			FROM  `*PREFIX*share_external`
181 181
 			WHERE `share_token` = ?');
182
-		$result = $getShare->execute([$token]);
183
-		$share = $result->fetch();
184
-		$result->closeCursor();
185
-		return $share;
186
-	}
187
-
188
-	private function fetchUserShare($parentId, $uid) {
189
-		$getShare = $this->connection->prepare('
182
+        $result = $getShare->execute([$token]);
183
+        $share = $result->fetch();
184
+        $result->closeCursor();
185
+        return $share;
186
+    }
187
+
188
+    private function fetchUserShare($parentId, $uid) {
189
+        $getShare = $this->connection->prepare('
190 190
 			SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
191 191
 			FROM  `*PREFIX*share_external`
192 192
 			WHERE `parent` = ? AND `user` = ?');
193
-		$result = $getShare->execute([$parentId, $uid]);
194
-		$share = $result->fetch();
195
-		$result->closeCursor();
196
-		if ($share !== false) {
197
-			return $share;
198
-		}
199
-		return null;
200
-	}
201
-
202
-	public function getShare(int $id, ?string $user = null): array|false {
203
-		$user = $user ?? $this->uid;
204
-		$share = $this->fetchShare($id);
205
-		if ($share === false) {
206
-			return false;
207
-		}
208
-
209
-		// check if the user is allowed to access it
210
-		if ($this->canAccessShare($share, $user)) {
211
-			return $share;
212
-		}
213
-
214
-		return false;
215
-	}
216
-
217
-	/**
218
-	 * Get share by token
219
-	 *
220
-	 * @param string $token
221
-	 * @return array|false
222
-	 */
223
-	public function getShareByToken(string $token): array|false {
224
-		$share = $this->fetchShareByToken($token);
225
-
226
-		// We do not check if the user is allowed to access it here,
227
-		// as this is not used from a user context.
228
-		if ($share === false) {
229
-			return false;
230
-		}
231
-
232
-		return $share;
233
-	}
234
-
235
-	private function canAccessShare(array $share, string $user): bool {
236
-		$validShare = isset($share['share_type']) && isset($share['user']);
237
-
238
-		if (!$validShare) {
239
-			return false;
240
-		}
241
-
242
-		// If the share is a user share, check if the user is the recipient
243
-		if ((int)$share['share_type'] === IShare::TYPE_USER
244
-			&& $share['user'] === $user) {
245
-			return true;
246
-		}
247
-
248
-		// If the share is a group share, check if the user is in the group
249
-		if ((int)$share['share_type'] === IShare::TYPE_GROUP) {
250
-			$parentId = (int)$share['parent'];
251
-			if ($parentId !== -1) {
252
-				// we just retrieved a sub-share, switch to the parent entry for verification
253
-				$groupShare = $this->fetchShare($parentId);
254
-			} else {
255
-				$groupShare = $share;
256
-			}
257
-
258
-			$user = $this->userManager->get($user);
259
-			if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
260
-				return true;
261
-			}
262
-		}
263
-
264
-		return false;
265
-	}
266
-
267
-	/**
268
-	 * Updates accepted flag in the database
269
-	 *
270
-	 * @param int $id
271
-	 */
272
-	private function updateAccepted(int $shareId, bool $accepted) : void {
273
-		$query = $this->connection->prepare('
193
+        $result = $getShare->execute([$parentId, $uid]);
194
+        $share = $result->fetch();
195
+        $result->closeCursor();
196
+        if ($share !== false) {
197
+            return $share;
198
+        }
199
+        return null;
200
+    }
201
+
202
+    public function getShare(int $id, ?string $user = null): array|false {
203
+        $user = $user ?? $this->uid;
204
+        $share = $this->fetchShare($id);
205
+        if ($share === false) {
206
+            return false;
207
+        }
208
+
209
+        // check if the user is allowed to access it
210
+        if ($this->canAccessShare($share, $user)) {
211
+            return $share;
212
+        }
213
+
214
+        return false;
215
+    }
216
+
217
+    /**
218
+     * Get share by token
219
+     *
220
+     * @param string $token
221
+     * @return array|false
222
+     */
223
+    public function getShareByToken(string $token): array|false {
224
+        $share = $this->fetchShareByToken($token);
225
+
226
+        // We do not check if the user is allowed to access it here,
227
+        // as this is not used from a user context.
228
+        if ($share === false) {
229
+            return false;
230
+        }
231
+
232
+        return $share;
233
+    }
234
+
235
+    private function canAccessShare(array $share, string $user): bool {
236
+        $validShare = isset($share['share_type']) && isset($share['user']);
237
+
238
+        if (!$validShare) {
239
+            return false;
240
+        }
241
+
242
+        // If the share is a user share, check if the user is the recipient
243
+        if ((int)$share['share_type'] === IShare::TYPE_USER
244
+            && $share['user'] === $user) {
245
+            return true;
246
+        }
247
+
248
+        // If the share is a group share, check if the user is in the group
249
+        if ((int)$share['share_type'] === IShare::TYPE_GROUP) {
250
+            $parentId = (int)$share['parent'];
251
+            if ($parentId !== -1) {
252
+                // we just retrieved a sub-share, switch to the parent entry for verification
253
+                $groupShare = $this->fetchShare($parentId);
254
+            } else {
255
+                $groupShare = $share;
256
+            }
257
+
258
+            $user = $this->userManager->get($user);
259
+            if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
260
+                return true;
261
+            }
262
+        }
263
+
264
+        return false;
265
+    }
266
+
267
+    /**
268
+     * Updates accepted flag in the database
269
+     *
270
+     * @param int $id
271
+     */
272
+    private function updateAccepted(int $shareId, bool $accepted) : void {
273
+        $query = $this->connection->prepare('
274 274
 			UPDATE `*PREFIX*share_external`
275 275
 			SET `accepted` = ?
276 276
 			WHERE `id` = ?');
277
-		$updateResult = $query->execute([$accepted ? 1 : 0, $shareId]);
278
-		$updateResult->closeCursor();
279
-	}
280
-
281
-	/**
282
-	 * accept server-to-server share
283
-	 *
284
-	 * @param int $id
285
-	 * @return bool True if the share could be accepted, false otherwise
286
-	 */
287
-	public function acceptShare(int $id, ?string $user = null) {
288
-		// If we're auto-accepting a share, we need to know the user id
289
-		// as there is no session available while processing the share
290
-		// from the remote server request.
291
-		$user = $user ?? $this->uid;
292
-		if ($user === null) {
293
-			$this->logger->error('No user specified for accepting share');
294
-			return false;
295
-		}
296
-
297
-		$share = $this->getShare($id, $user);
298
-		$result = false;
299
-
300
-		if ($share) {
301
-			\OC_Util::setupFS($user);
302
-			$shareFolder = Helper::getShareFolder(null, $user);
303
-			$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
304
-			$mountPoint = Filesystem::normalizePath($mountPoint);
305
-			$hash = md5($mountPoint);
306
-			$userShareAccepted = false;
307
-
308
-			if ((int)$share['share_type'] === IShare::TYPE_USER) {
309
-				$acceptShare = $this->connection->prepare('
277
+        $updateResult = $query->execute([$accepted ? 1 : 0, $shareId]);
278
+        $updateResult->closeCursor();
279
+    }
280
+
281
+    /**
282
+     * accept server-to-server share
283
+     *
284
+     * @param int $id
285
+     * @return bool True if the share could be accepted, false otherwise
286
+     */
287
+    public function acceptShare(int $id, ?string $user = null) {
288
+        // If we're auto-accepting a share, we need to know the user id
289
+        // as there is no session available while processing the share
290
+        // from the remote server request.
291
+        $user = $user ?? $this->uid;
292
+        if ($user === null) {
293
+            $this->logger->error('No user specified for accepting share');
294
+            return false;
295
+        }
296
+
297
+        $share = $this->getShare($id, $user);
298
+        $result = false;
299
+
300
+        if ($share) {
301
+            \OC_Util::setupFS($user);
302
+            $shareFolder = Helper::getShareFolder(null, $user);
303
+            $mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
304
+            $mountPoint = Filesystem::normalizePath($mountPoint);
305
+            $hash = md5($mountPoint);
306
+            $userShareAccepted = false;
307
+
308
+            if ((int)$share['share_type'] === IShare::TYPE_USER) {
309
+                $acceptShare = $this->connection->prepare('
310 310
 				UPDATE `*PREFIX*share_external`
311 311
 				SET `accepted` = ?,
312 312
 					`mountpoint` = ?,
313 313
 					`mountpoint_hash` = ?
314 314
 				WHERE `id` = ? AND `user` = ?');
315
-				$userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
316
-			} else {
317
-				$parentId = (int)$share['parent'];
318
-				if ($parentId !== -1) {
319
-					// this is the sub-share
320
-					$subshare = $share;
321
-				} else {
322
-					$subshare = $this->fetchUserShare($id, $user);
323
-				}
324
-
325
-				if ($subshare !== null) {
326
-					try {
327
-						$acceptShare = $this->connection->prepare('
315
+                $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
316
+            } else {
317
+                $parentId = (int)$share['parent'];
318
+                if ($parentId !== -1) {
319
+                    // this is the sub-share
320
+                    $subshare = $share;
321
+                } else {
322
+                    $subshare = $this->fetchUserShare($id, $user);
323
+                }
324
+
325
+                if ($subshare !== null) {
326
+                    try {
327
+                        $acceptShare = $this->connection->prepare('
328 328
 						UPDATE `*PREFIX*share_external`
329 329
 						SET `accepted` = ?,
330 330
 							`mountpoint` = ?,
331 331
 							`mountpoint_hash` = ?
332 332
 						WHERE `id` = ? AND `user` = ?');
333
-						$acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
334
-						$result = true;
335
-					} catch (Exception $e) {
336
-						$this->logger->emergency('Could not update share', ['exception' => $e]);
337
-						$result = false;
338
-					}
339
-				} else {
340
-					try {
341
-						$this->writeShareToDb(
342
-							$share['remote'],
343
-							$share['share_token'],
344
-							$share['password'],
345
-							$share['name'],
346
-							$share['owner'],
347
-							$user,
348
-							$mountPoint, $hash, 1,
349
-							$share['remote_id'],
350
-							$id,
351
-							$share['share_type']);
352
-						$result = true;
353
-					} catch (Exception $e) {
354
-						$this->logger->emergency('Could not create share', ['exception' => $e]);
355
-						$result = false;
356
-					}
357
-				}
358
-			}
359
-
360
-			if ($userShareAccepted !== false) {
361
-				$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
362
-				$event = new FederatedShareAddedEvent($share['remote']);
363
-				$this->eventDispatcher->dispatchTyped($event);
364
-				$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
365
-				$result = true;
366
-			}
367
-		}
368
-
369
-		// Make sure the user has no notification for something that does not exist anymore.
370
-		$this->processNotification($id, $user);
371
-
372
-		return $result;
373
-	}
374
-
375
-	/**
376
-	 * decline server-to-server share
377
-	 *
378
-	 * @param int $id
379
-	 * @return bool True if the share could be declined, false otherwise
380
-	 */
381
-	public function declineShare(int $id, ?string $user = null) {
382
-		$user = $user ?? $this->uid;
383
-		if ($user === null) {
384
-			$this->logger->error('No user specified for declining share');
385
-			return false;
386
-		}
387
-
388
-		$share = $this->getShare($id, $user);
389
-		$result = false;
390
-
391
-		if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
392
-			$removeShare = $this->connection->prepare('
333
+                        $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
334
+                        $result = true;
335
+                    } catch (Exception $e) {
336
+                        $this->logger->emergency('Could not update share', ['exception' => $e]);
337
+                        $result = false;
338
+                    }
339
+                } else {
340
+                    try {
341
+                        $this->writeShareToDb(
342
+                            $share['remote'],
343
+                            $share['share_token'],
344
+                            $share['password'],
345
+                            $share['name'],
346
+                            $share['owner'],
347
+                            $user,
348
+                            $mountPoint, $hash, 1,
349
+                            $share['remote_id'],
350
+                            $id,
351
+                            $share['share_type']);
352
+                        $result = true;
353
+                    } catch (Exception $e) {
354
+                        $this->logger->emergency('Could not create share', ['exception' => $e]);
355
+                        $result = false;
356
+                    }
357
+                }
358
+            }
359
+
360
+            if ($userShareAccepted !== false) {
361
+                $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
362
+                $event = new FederatedShareAddedEvent($share['remote']);
363
+                $this->eventDispatcher->dispatchTyped($event);
364
+                $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
365
+                $result = true;
366
+            }
367
+        }
368
+
369
+        // Make sure the user has no notification for something that does not exist anymore.
370
+        $this->processNotification($id, $user);
371
+
372
+        return $result;
373
+    }
374
+
375
+    /**
376
+     * decline server-to-server share
377
+     *
378
+     * @param int $id
379
+     * @return bool True if the share could be declined, false otherwise
380
+     */
381
+    public function declineShare(int $id, ?string $user = null) {
382
+        $user = $user ?? $this->uid;
383
+        if ($user === null) {
384
+            $this->logger->error('No user specified for declining share');
385
+            return false;
386
+        }
387
+
388
+        $share = $this->getShare($id, $user);
389
+        $result = false;
390
+
391
+        if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
392
+            $removeShare = $this->connection->prepare('
393 393
 				DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
394
-			$removeShare->execute([$id, $user]);
395
-			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
396
-
397
-			$this->processNotification($id, $user);
398
-			$result = true;
399
-		} elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
400
-			$parentId = (int)$share['parent'];
401
-			if ($parentId !== -1) {
402
-				// this is the sub-share
403
-				$subshare = $share;
404
-			} else {
405
-				$subshare = $this->fetchUserShare($id, $user);
406
-			}
407
-
408
-			if ($subshare !== null) {
409
-				try {
410
-					$this->updateAccepted((int)$subshare['id'], false);
411
-					$result = true;
412
-				} catch (Exception $e) {
413
-					$this->logger->emergency('Could not update share', ['exception' => $e]);
414
-					$result = false;
415
-				}
416
-			} else {
417
-				try {
418
-					$this->writeShareToDb(
419
-						$share['remote'],
420
-						$share['share_token'],
421
-						$share['password'],
422
-						$share['name'],
423
-						$share['owner'],
424
-						$user,
425
-						$share['mountpoint'],
426
-						$share['mountpoint_hash'],
427
-						0,
428
-						$share['remote_id'],
429
-						$id,
430
-						$share['share_type']);
431
-					$result = true;
432
-				} catch (Exception $e) {
433
-					$this->logger->emergency('Could not create share', ['exception' => $e]);
434
-					$result = false;
435
-				}
436
-			}
437
-			$this->processNotification($id, $user);
438
-		}
439
-
440
-		return $result;
441
-	}
442
-
443
-	public function processNotification(int $remoteShare, ?string $user = null): void {
444
-		$user = $user ?? $this->uid;
445
-		if ($user === null) {
446
-			$this->logger->error('No user specified for processing notification');
447
-			return;
448
-		}
449
-
450
-		$share = $this->fetchShare($remoteShare);
451
-		if ($share === false) {
452
-			return;
453
-		}
454
-
455
-		$filter = $this->notificationManager->createNotification();
456
-		$filter->setApp('files_sharing')
457
-			->setUser($user)
458
-			->setObject('remote_share', (string)$remoteShare);
459
-		$this->notificationManager->markProcessed($filter);
460
-	}
461
-
462
-	/**
463
-	 * inform remote server whether server-to-server share was accepted/declined
464
-	 *
465
-	 * @param string $remote
466
-	 * @param string $token
467
-	 * @param string $remoteId Share id on the remote host
468
-	 * @param string $feedback
469
-	 * @return boolean
470
-	 */
471
-	private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) {
472
-		$result = $this->tryOCMEndPoint($remote, $token, $remoteId, $feedback);
473
-
474
-		if (is_array($result)) {
475
-			return true;
476
-		}
477
-
478
-		$federationEndpoints = $this->discoveryService->discover($remote, 'FEDERATED_SHARING');
479
-		$endpoint = $federationEndpoints['share'] ?? '/ocs/v2.php/cloud/shares';
480
-
481
-		$url = rtrim($remote, '/') . $endpoint . '/' . $remoteId . '/' . $feedback . '?format=' . Share::RESPONSE_FORMAT;
482
-		$fields = ['token' => $token];
483
-
484
-		$client = $this->clientService->newClient();
485
-
486
-		try {
487
-			$response = $client->post(
488
-				$url,
489
-				[
490
-					'body' => $fields,
491
-					'connect_timeout' => 10,
492
-				]
493
-			);
494
-		} catch (\Exception $e) {
495
-			return false;
496
-		}
497
-
498
-		$status = json_decode($response->getBody(), true);
499
-
500
-		return ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200);
501
-	}
502
-
503
-	/**
504
-	 * try send accept message to ocm end-point
505
-	 *
506
-	 * @param string $remoteDomain
507
-	 * @param string $token
508
-	 * @param string $remoteId id of the share
509
-	 * @param string $feedback
510
-	 * @return array|false
511
-	 */
512
-	protected function tryOCMEndPoint($remoteDomain, $token, $remoteId, $feedback) {
513
-		switch ($feedback) {
514
-			case 'accept':
515
-				$notification = $this->cloudFederationFactory->getCloudFederationNotification();
516
-				$notification->setMessage(
517
-					'SHARE_ACCEPTED',
518
-					'file',
519
-					$remoteId,
520
-					[
521
-						'sharedSecret' => $token,
522
-						'message' => 'Recipient accept the share'
523
-					]
524
-
525
-				);
526
-				return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification);
527
-			case 'decline':
528
-				$notification = $this->cloudFederationFactory->getCloudFederationNotification();
529
-				$notification->setMessage(
530
-					'SHARE_DECLINED',
531
-					'file',
532
-					$remoteId,
533
-					[
534
-						'sharedSecret' => $token,
535
-						'message' => 'Recipient declined the share'
536
-					]
537
-
538
-				);
539
-				return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification);
540
-		}
541
-
542
-		return false;
543
-	}
544
-
545
-
546
-	/**
547
-	 * remove '/user/files' from the path and trailing slashes
548
-	 *
549
-	 * @param string $path
550
-	 * @return string
551
-	 */
552
-	protected function stripPath($path) {
553
-		$prefix = '/' . $this->uid . '/files';
554
-		return rtrim(substr($path, strlen($prefix)), '/');
555
-	}
556
-
557
-	public function getMount($data, ?string $user = null) {
558
-		$user = $user ?? $this->uid;
559
-		$data['manager'] = $this;
560
-		$mountPoint = '/' . $user . '/files' . $data['mountpoint'];
561
-		$data['mountpoint'] = $mountPoint;
562
-		$data['certificateManager'] = \OC::$server->getCertificateManager();
563
-		return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
564
-	}
565
-
566
-	/**
567
-	 * @param array $data
568
-	 * @return Mount
569
-	 */
570
-	protected function mountShare($data, ?string $user = null) {
571
-		$mount = $this->getMount($data, $user);
572
-		$this->mountManager->addMount($mount);
573
-		return $mount;
574
-	}
575
-
576
-	/**
577
-	 * @return \OC\Files\Mount\Manager
578
-	 */
579
-	public function getMountManager() {
580
-		return $this->mountManager;
581
-	}
582
-
583
-	/**
584
-	 * @param string $source
585
-	 * @param string $target
586
-	 * @return bool
587
-	 */
588
-	public function setMountPoint($source, $target) {
589
-		$source = $this->stripPath($source);
590
-		$target = $this->stripPath($target);
591
-		$sourceHash = md5($source);
592
-		$targetHash = md5($target);
593
-
594
-		$query = $this->connection->prepare('
394
+            $removeShare->execute([$id, $user]);
395
+            $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
396
+
397
+            $this->processNotification($id, $user);
398
+            $result = true;
399
+        } elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
400
+            $parentId = (int)$share['parent'];
401
+            if ($parentId !== -1) {
402
+                // this is the sub-share
403
+                $subshare = $share;
404
+            } else {
405
+                $subshare = $this->fetchUserShare($id, $user);
406
+            }
407
+
408
+            if ($subshare !== null) {
409
+                try {
410
+                    $this->updateAccepted((int)$subshare['id'], false);
411
+                    $result = true;
412
+                } catch (Exception $e) {
413
+                    $this->logger->emergency('Could not update share', ['exception' => $e]);
414
+                    $result = false;
415
+                }
416
+            } else {
417
+                try {
418
+                    $this->writeShareToDb(
419
+                        $share['remote'],
420
+                        $share['share_token'],
421
+                        $share['password'],
422
+                        $share['name'],
423
+                        $share['owner'],
424
+                        $user,
425
+                        $share['mountpoint'],
426
+                        $share['mountpoint_hash'],
427
+                        0,
428
+                        $share['remote_id'],
429
+                        $id,
430
+                        $share['share_type']);
431
+                    $result = true;
432
+                } catch (Exception $e) {
433
+                    $this->logger->emergency('Could not create share', ['exception' => $e]);
434
+                    $result = false;
435
+                }
436
+            }
437
+            $this->processNotification($id, $user);
438
+        }
439
+
440
+        return $result;
441
+    }
442
+
443
+    public function processNotification(int $remoteShare, ?string $user = null): void {
444
+        $user = $user ?? $this->uid;
445
+        if ($user === null) {
446
+            $this->logger->error('No user specified for processing notification');
447
+            return;
448
+        }
449
+
450
+        $share = $this->fetchShare($remoteShare);
451
+        if ($share === false) {
452
+            return;
453
+        }
454
+
455
+        $filter = $this->notificationManager->createNotification();
456
+        $filter->setApp('files_sharing')
457
+            ->setUser($user)
458
+            ->setObject('remote_share', (string)$remoteShare);
459
+        $this->notificationManager->markProcessed($filter);
460
+    }
461
+
462
+    /**
463
+     * inform remote server whether server-to-server share was accepted/declined
464
+     *
465
+     * @param string $remote
466
+     * @param string $token
467
+     * @param string $remoteId Share id on the remote host
468
+     * @param string $feedback
469
+     * @return boolean
470
+     */
471
+    private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) {
472
+        $result = $this->tryOCMEndPoint($remote, $token, $remoteId, $feedback);
473
+
474
+        if (is_array($result)) {
475
+            return true;
476
+        }
477
+
478
+        $federationEndpoints = $this->discoveryService->discover($remote, 'FEDERATED_SHARING');
479
+        $endpoint = $federationEndpoints['share'] ?? '/ocs/v2.php/cloud/shares';
480
+
481
+        $url = rtrim($remote, '/') . $endpoint . '/' . $remoteId . '/' . $feedback . '?format=' . Share::RESPONSE_FORMAT;
482
+        $fields = ['token' => $token];
483
+
484
+        $client = $this->clientService->newClient();
485
+
486
+        try {
487
+            $response = $client->post(
488
+                $url,
489
+                [
490
+                    'body' => $fields,
491
+                    'connect_timeout' => 10,
492
+                ]
493
+            );
494
+        } catch (\Exception $e) {
495
+            return false;
496
+        }
497
+
498
+        $status = json_decode($response->getBody(), true);
499
+
500
+        return ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200);
501
+    }
502
+
503
+    /**
504
+     * try send accept message to ocm end-point
505
+     *
506
+     * @param string $remoteDomain
507
+     * @param string $token
508
+     * @param string $remoteId id of the share
509
+     * @param string $feedback
510
+     * @return array|false
511
+     */
512
+    protected function tryOCMEndPoint($remoteDomain, $token, $remoteId, $feedback) {
513
+        switch ($feedback) {
514
+            case 'accept':
515
+                $notification = $this->cloudFederationFactory->getCloudFederationNotification();
516
+                $notification->setMessage(
517
+                    'SHARE_ACCEPTED',
518
+                    'file',
519
+                    $remoteId,
520
+                    [
521
+                        'sharedSecret' => $token,
522
+                        'message' => 'Recipient accept the share'
523
+                    ]
524
+
525
+                );
526
+                return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification);
527
+            case 'decline':
528
+                $notification = $this->cloudFederationFactory->getCloudFederationNotification();
529
+                $notification->setMessage(
530
+                    'SHARE_DECLINED',
531
+                    'file',
532
+                    $remoteId,
533
+                    [
534
+                        'sharedSecret' => $token,
535
+                        'message' => 'Recipient declined the share'
536
+                    ]
537
+
538
+                );
539
+                return $this->cloudFederationProviderManager->sendNotification($remoteDomain, $notification);
540
+        }
541
+
542
+        return false;
543
+    }
544
+
545
+
546
+    /**
547
+     * remove '/user/files' from the path and trailing slashes
548
+     *
549
+     * @param string $path
550
+     * @return string
551
+     */
552
+    protected function stripPath($path) {
553
+        $prefix = '/' . $this->uid . '/files';
554
+        return rtrim(substr($path, strlen($prefix)), '/');
555
+    }
556
+
557
+    public function getMount($data, ?string $user = null) {
558
+        $user = $user ?? $this->uid;
559
+        $data['manager'] = $this;
560
+        $mountPoint = '/' . $user . '/files' . $data['mountpoint'];
561
+        $data['mountpoint'] = $mountPoint;
562
+        $data['certificateManager'] = \OC::$server->getCertificateManager();
563
+        return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
564
+    }
565
+
566
+    /**
567
+     * @param array $data
568
+     * @return Mount
569
+     */
570
+    protected function mountShare($data, ?string $user = null) {
571
+        $mount = $this->getMount($data, $user);
572
+        $this->mountManager->addMount($mount);
573
+        return $mount;
574
+    }
575
+
576
+    /**
577
+     * @return \OC\Files\Mount\Manager
578
+     */
579
+    public function getMountManager() {
580
+        return $this->mountManager;
581
+    }
582
+
583
+    /**
584
+     * @param string $source
585
+     * @param string $target
586
+     * @return bool
587
+     */
588
+    public function setMountPoint($source, $target) {
589
+        $source = $this->stripPath($source);
590
+        $target = $this->stripPath($target);
591
+        $sourceHash = md5($source);
592
+        $targetHash = md5($target);
593
+
594
+        $query = $this->connection->prepare('
595 595
 			UPDATE `*PREFIX*share_external`
596 596
 			SET `mountpoint` = ?, `mountpoint_hash` = ?
597 597
 			WHERE `mountpoint_hash` = ?
598 598
 			AND `user` = ?
599 599
 		');
600
-		$result = (bool)$query->execute([$target, $targetHash, $sourceHash, $this->uid]);
601
-
602
-		$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
603
-
604
-		return $result;
605
-	}
606
-
607
-	public function removeShare($mountPoint): bool {
608
-		try {
609
-			$mountPointObj = $this->mountManager->find($mountPoint);
610
-		} catch (NotFoundException $e) {
611
-			$this->logger->error('Mount point to remove share not found', ['mountPoint' => $mountPoint]);
612
-			return false;
613
-		}
614
-		if (!$mountPointObj instanceof Mount) {
615
-			$this->logger->error('Mount point to remove share is not an external share, share probably doesn\'t exist', ['mountPoint' => $mountPoint]);
616
-			return false;
617
-		}
618
-		$id = $mountPointObj->getStorage()->getCache()->getId('');
619
-
620
-		$mountPoint = $this->stripPath($mountPoint);
621
-		$hash = md5($mountPoint);
622
-
623
-		try {
624
-			$getShare = $this->connection->prepare('
600
+        $result = (bool)$query->execute([$target, $targetHash, $sourceHash, $this->uid]);
601
+
602
+        $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
603
+
604
+        return $result;
605
+    }
606
+
607
+    public function removeShare($mountPoint): bool {
608
+        try {
609
+            $mountPointObj = $this->mountManager->find($mountPoint);
610
+        } catch (NotFoundException $e) {
611
+            $this->logger->error('Mount point to remove share not found', ['mountPoint' => $mountPoint]);
612
+            return false;
613
+        }
614
+        if (!$mountPointObj instanceof Mount) {
615
+            $this->logger->error('Mount point to remove share is not an external share, share probably doesn\'t exist', ['mountPoint' => $mountPoint]);
616
+            return false;
617
+        }
618
+        $id = $mountPointObj->getStorage()->getCache()->getId('');
619
+
620
+        $mountPoint = $this->stripPath($mountPoint);
621
+        $hash = md5($mountPoint);
622
+
623
+        try {
624
+            $getShare = $this->connection->prepare('
625 625
 				SELECT `remote`, `share_token`, `remote_id`, `share_type`, `id`
626 626
 				FROM  `*PREFIX*share_external`
627 627
 				WHERE `mountpoint_hash` = ? AND `user` = ?');
628
-			$result = $getShare->execute([$hash, $this->uid]);
629
-			$share = $result->fetch();
630
-			$result->closeCursor();
631
-			if ($share !== false && (int)$share['share_type'] === IShare::TYPE_USER) {
632
-				try {
633
-					$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
634
-				} catch (\Throwable $e) {
635
-					// if we fail to notify the remote (probably cause the remote is down)
636
-					// we still want the share to be gone to prevent undeletable remotes
637
-				}
638
-
639
-				$query = $this->connection->prepare('
628
+            $result = $getShare->execute([$hash, $this->uid]);
629
+            $share = $result->fetch();
630
+            $result->closeCursor();
631
+            if ($share !== false && (int)$share['share_type'] === IShare::TYPE_USER) {
632
+                try {
633
+                    $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
634
+                } catch (\Throwable $e) {
635
+                    // if we fail to notify the remote (probably cause the remote is down)
636
+                    // we still want the share to be gone to prevent undeletable remotes
637
+                }
638
+
639
+                $query = $this->connection->prepare('
640 640
 					DELETE FROM `*PREFIX*share_external`
641 641
 					WHERE `id` = ?
642 642
 				');
643
-				$deleteResult = $query->execute([(int)$share['id']]);
644
-				$deleteResult->closeCursor();
645
-			} elseif ($share !== false && (int)$share['share_type'] === IShare::TYPE_GROUP) {
646
-				$this->updateAccepted((int)$share['id'], false);
647
-			}
648
-
649
-			$this->removeReShares($id);
650
-		} catch (\Doctrine\DBAL\Exception $ex) {
651
-			$this->logger->emergency('Could not update share', ['exception' => $ex]);
652
-			return false;
653
-		}
654
-
655
-		return true;
656
-	}
657
-
658
-	/**
659
-	 * remove re-shares from share table and mapping in the federated_reshares table
660
-	 *
661
-	 * @param $mountPointId
662
-	 */
663
-	protected function removeReShares($mountPointId) {
664
-		$selectQuery = $this->connection->getQueryBuilder();
665
-		$query = $this->connection->getQueryBuilder();
666
-		$selectQuery->select('id')->from('share')
667
-			->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId)));
668
-		$select = $selectQuery->getSQL();
669
-
670
-
671
-		$query->delete('federated_reshares')
672
-			->where($query->expr()->in('share_id', $query->createFunction($select)));
673
-		$query->execute();
674
-
675
-		$deleteReShares = $this->connection->getQueryBuilder();
676
-		$deleteReShares->delete('share')
677
-			->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId)));
678
-		$deleteReShares->execute();
679
-	}
680
-
681
-	/**
682
-	 * remove all shares for user $uid if the user was deleted
683
-	 *
684
-	 * @param string $uid
685
-	 */
686
-	public function removeUserShares($uid): bool {
687
-		try {
688
-			// TODO: use query builder
689
-			$getShare = $this->connection->prepare('
643
+                $deleteResult = $query->execute([(int)$share['id']]);
644
+                $deleteResult->closeCursor();
645
+            } elseif ($share !== false && (int)$share['share_type'] === IShare::TYPE_GROUP) {
646
+                $this->updateAccepted((int)$share['id'], false);
647
+            }
648
+
649
+            $this->removeReShares($id);
650
+        } catch (\Doctrine\DBAL\Exception $ex) {
651
+            $this->logger->emergency('Could not update share', ['exception' => $ex]);
652
+            return false;
653
+        }
654
+
655
+        return true;
656
+    }
657
+
658
+    /**
659
+     * remove re-shares from share table and mapping in the federated_reshares table
660
+     *
661
+     * @param $mountPointId
662
+     */
663
+    protected function removeReShares($mountPointId) {
664
+        $selectQuery = $this->connection->getQueryBuilder();
665
+        $query = $this->connection->getQueryBuilder();
666
+        $selectQuery->select('id')->from('share')
667
+            ->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId)));
668
+        $select = $selectQuery->getSQL();
669
+
670
+
671
+        $query->delete('federated_reshares')
672
+            ->where($query->expr()->in('share_id', $query->createFunction($select)));
673
+        $query->execute();
674
+
675
+        $deleteReShares = $this->connection->getQueryBuilder();
676
+        $deleteReShares->delete('share')
677
+            ->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId)));
678
+        $deleteReShares->execute();
679
+    }
680
+
681
+    /**
682
+     * remove all shares for user $uid if the user was deleted
683
+     *
684
+     * @param string $uid
685
+     */
686
+    public function removeUserShares($uid): bool {
687
+        try {
688
+            // TODO: use query builder
689
+            $getShare = $this->connection->prepare('
690 690
 				SELECT `id`, `remote`, `share_type`, `share_token`, `remote_id`
691 691
 				FROM  `*PREFIX*share_external`
692 692
 				WHERE `user` = ?
693 693
 				AND `share_type` = ?');
694
-			$result = $getShare->execute([$uid, IShare::TYPE_USER]);
695
-			$shares = $result->fetchAll();
696
-			$result->closeCursor();
697
-
698
-			foreach ($shares as $share) {
699
-				$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
700
-			}
701
-
702
-			$qb = $this->connection->getQueryBuilder();
703
-			$qb->delete('share_external')
704
-				// user field can specify a user or a group
705
-				->where($qb->expr()->eq('user', $qb->createNamedParameter($uid)))
706
-				->andWhere(
707
-					$qb->expr()->orX(
708
-						// delete direct shares
709
-						$qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_USER)),
710
-						// delete sub-shares of group shares for that user
711
-						$qb->expr()->andX(
712
-							$qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_GROUP)),
713
-							$qb->expr()->neq('parent', $qb->expr()->literal(-1)),
714
-						)
715
-					)
716
-				);
717
-			$qb->execute();
718
-		} catch (\Doctrine\DBAL\Exception $ex) {
719
-			$this->logger->emergency('Could not delete user shares', ['exception' => $ex]);
720
-			return false;
721
-		}
722
-
723
-		return true;
724
-	}
725
-
726
-	public function removeGroupShares($gid): bool {
727
-		try {
728
-			$getShare = $this->connection->prepare('
694
+            $result = $getShare->execute([$uid, IShare::TYPE_USER]);
695
+            $shares = $result->fetchAll();
696
+            $result->closeCursor();
697
+
698
+            foreach ($shares as $share) {
699
+                $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
700
+            }
701
+
702
+            $qb = $this->connection->getQueryBuilder();
703
+            $qb->delete('share_external')
704
+                // user field can specify a user or a group
705
+                ->where($qb->expr()->eq('user', $qb->createNamedParameter($uid)))
706
+                ->andWhere(
707
+                    $qb->expr()->orX(
708
+                        // delete direct shares
709
+                        $qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_USER)),
710
+                        // delete sub-shares of group shares for that user
711
+                        $qb->expr()->andX(
712
+                            $qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_GROUP)),
713
+                            $qb->expr()->neq('parent', $qb->expr()->literal(-1)),
714
+                        )
715
+                    )
716
+                );
717
+            $qb->execute();
718
+        } catch (\Doctrine\DBAL\Exception $ex) {
719
+            $this->logger->emergency('Could not delete user shares', ['exception' => $ex]);
720
+            return false;
721
+        }
722
+
723
+        return true;
724
+    }
725
+
726
+    public function removeGroupShares($gid): bool {
727
+        try {
728
+            $getShare = $this->connection->prepare('
729 729
 				SELECT `id`, `remote`, `share_type`, `share_token`, `remote_id`
730 730
 				FROM  `*PREFIX*share_external`
731 731
 				WHERE `user` = ?
732 732
 				AND `share_type` = ?');
733
-			$result = $getShare->execute([$gid, IShare::TYPE_GROUP]);
734
-			$shares = $result->fetchAll();
735
-			$result->closeCursor();
736
-
737
-			$deletedGroupShares = [];
738
-			$qb = $this->connection->getQueryBuilder();
739
-			// delete group share entry and matching sub-entries
740
-			$qb->delete('share_external')
741
-				->where(
742
-					$qb->expr()->orX(
743
-						$qb->expr()->eq('id', $qb->createParameter('share_id')),
744
-						$qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
745
-					)
746
-				);
747
-
748
-			foreach ($shares as $share) {
749
-				$qb->setParameter('share_id', $share['id']);
750
-				$qb->setParameter('share_parent_id', $share['id']);
751
-				$qb->execute();
752
-			}
753
-		} catch (\Doctrine\DBAL\Exception $ex) {
754
-			$this->logger->emergency('Could not delete user shares', ['exception' => $ex]);
755
-			return false;
756
-		}
757
-
758
-		return true;
759
-	}
760
-
761
-	/**
762
-	 * return a list of shares which are not yet accepted by the user
763
-	 *
764
-	 * @return list<Files_SharingRemoteShare> list of open server-to-server shares
765
-	 */
766
-	public function getOpenShares() {
767
-		return $this->getShares(false);
768
-	}
769
-
770
-	/**
771
-	 * return a list of shares which are accepted by the user
772
-	 *
773
-	 * @return list<Files_SharingRemoteShare> list of accepted server-to-server shares
774
-	 */
775
-	public function getAcceptedShares() {
776
-		return $this->getShares(true);
777
-	}
778
-
779
-	/**
780
-	 * return a list of shares for the user
781
-	 *
782
-	 * @param bool|null $accepted True for accepted only,
783
-	 *                            false for not accepted,
784
-	 *                            null for all shares of the user
785
-	 * @return list<Files_SharingRemoteShare> list of open server-to-server shares
786
-	 */
787
-	private function getShares($accepted) {
788
-		// Not allowing providing a user here,
789
-		// as we only want to retrieve shares for the current user.
790
-		$user = $this->userManager->get($this->uid);
791
-		$groups = $this->groupManager->getUserGroups($user);
792
-		$userGroups = [];
793
-		foreach ($groups as $group) {
794
-			$userGroups[] = $group->getGID();
795
-		}
796
-
797
-		$qb = $this->connection->getQueryBuilder();
798
-		$qb->select('id', 'share_type', 'parent', 'remote', 'remote_id', 'share_token', 'name', 'owner', 'user', 'mountpoint', 'accepted')
799
-			->from('share_external')
800
-			->where(
801
-				$qb->expr()->orX(
802
-					$qb->expr()->eq('user', $qb->createNamedParameter($this->uid)),
803
-					$qb->expr()->in(
804
-						'user',
805
-						$qb->createNamedParameter($userGroups, IQueryBuilder::PARAM_STR_ARRAY)
806
-					)
807
-				)
808
-			)
809
-			->orderBy('id', 'ASC');
810
-
811
-		try {
812
-			$result = $qb->execute();
813
-			$shares = $result->fetchAll();
814
-			$result->closeCursor();
815
-
816
-			// remove parent group share entry if we have a specific user share entry for the user
817
-			$toRemove = [];
818
-			foreach ($shares as $share) {
819
-				if ((int)$share['share_type'] === IShare::TYPE_GROUP && (int)$share['parent'] > 0) {
820
-					$toRemove[] = $share['parent'];
821
-				}
822
-			}
823
-			$shares = array_filter($shares, function ($share) use ($toRemove) {
824
-				return !in_array($share['id'], $toRemove, true);
825
-			});
826
-
827
-			if (!is_null($accepted)) {
828
-				$shares = array_filter($shares, function ($share) use ($accepted) {
829
-					return (bool)$share['accepted'] === $accepted;
830
-				});
831
-			}
832
-			return array_values($shares);
833
-		} catch (\Doctrine\DBAL\Exception $e) {
834
-			$this->logger->emergency('Error when retrieving shares', ['exception' => $e]);
835
-			return [];
836
-		}
837
-	}
733
+            $result = $getShare->execute([$gid, IShare::TYPE_GROUP]);
734
+            $shares = $result->fetchAll();
735
+            $result->closeCursor();
736
+
737
+            $deletedGroupShares = [];
738
+            $qb = $this->connection->getQueryBuilder();
739
+            // delete group share entry and matching sub-entries
740
+            $qb->delete('share_external')
741
+                ->where(
742
+                    $qb->expr()->orX(
743
+                        $qb->expr()->eq('id', $qb->createParameter('share_id')),
744
+                        $qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
745
+                    )
746
+                );
747
+
748
+            foreach ($shares as $share) {
749
+                $qb->setParameter('share_id', $share['id']);
750
+                $qb->setParameter('share_parent_id', $share['id']);
751
+                $qb->execute();
752
+            }
753
+        } catch (\Doctrine\DBAL\Exception $ex) {
754
+            $this->logger->emergency('Could not delete user shares', ['exception' => $ex]);
755
+            return false;
756
+        }
757
+
758
+        return true;
759
+    }
760
+
761
+    /**
762
+     * return a list of shares which are not yet accepted by the user
763
+     *
764
+     * @return list<Files_SharingRemoteShare> list of open server-to-server shares
765
+     */
766
+    public function getOpenShares() {
767
+        return $this->getShares(false);
768
+    }
769
+
770
+    /**
771
+     * return a list of shares which are accepted by the user
772
+     *
773
+     * @return list<Files_SharingRemoteShare> list of accepted server-to-server shares
774
+     */
775
+    public function getAcceptedShares() {
776
+        return $this->getShares(true);
777
+    }
778
+
779
+    /**
780
+     * return a list of shares for the user
781
+     *
782
+     * @param bool|null $accepted True for accepted only,
783
+     *                            false for not accepted,
784
+     *                            null for all shares of the user
785
+     * @return list<Files_SharingRemoteShare> list of open server-to-server shares
786
+     */
787
+    private function getShares($accepted) {
788
+        // Not allowing providing a user here,
789
+        // as we only want to retrieve shares for the current user.
790
+        $user = $this->userManager->get($this->uid);
791
+        $groups = $this->groupManager->getUserGroups($user);
792
+        $userGroups = [];
793
+        foreach ($groups as $group) {
794
+            $userGroups[] = $group->getGID();
795
+        }
796
+
797
+        $qb = $this->connection->getQueryBuilder();
798
+        $qb->select('id', 'share_type', 'parent', 'remote', 'remote_id', 'share_token', 'name', 'owner', 'user', 'mountpoint', 'accepted')
799
+            ->from('share_external')
800
+            ->where(
801
+                $qb->expr()->orX(
802
+                    $qb->expr()->eq('user', $qb->createNamedParameter($this->uid)),
803
+                    $qb->expr()->in(
804
+                        'user',
805
+                        $qb->createNamedParameter($userGroups, IQueryBuilder::PARAM_STR_ARRAY)
806
+                    )
807
+                )
808
+            )
809
+            ->orderBy('id', 'ASC');
810
+
811
+        try {
812
+            $result = $qb->execute();
813
+            $shares = $result->fetchAll();
814
+            $result->closeCursor();
815
+
816
+            // remove parent group share entry if we have a specific user share entry for the user
817
+            $toRemove = [];
818
+            foreach ($shares as $share) {
819
+                if ((int)$share['share_type'] === IShare::TYPE_GROUP && (int)$share['parent'] > 0) {
820
+                    $toRemove[] = $share['parent'];
821
+                }
822
+            }
823
+            $shares = array_filter($shares, function ($share) use ($toRemove) {
824
+                return !in_array($share['id'], $toRemove, true);
825
+            });
826
+
827
+            if (!is_null($accepted)) {
828
+                $shares = array_filter($shares, function ($share) use ($accepted) {
829
+                    return (bool)$share['accepted'] === $accepted;
830
+                });
831
+            }
832
+            return array_values($shares);
833
+        } catch (\Doctrine\DBAL\Exception $e) {
834
+            $this->logger->emergency('Error when retrieving shares', ['exception' => $e]);
835
+            return [];
836
+        }
837
+    }
838 838
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/ApiTest.php 1 patch
Indentation   +1417 added lines, -1417 removed lines patch added patch discarded remove patch
@@ -45,1434 +45,1434 @@
 block discarded – undo
45 45
  * TODO: convert to real integration tests
46 46
  */
47 47
 class ApiTest extends TestCase {
48
-	public const TEST_FOLDER_NAME = '/folder_share_api_test';
49
-	public const APP_NAME = 'files_sharing';
50
-
51
-	private static $tempStorage;
52
-
53
-	/** @var Folder */
54
-	private $userFolder;
55
-
56
-	/** @var string */
57
-	private $subsubfolder;
58
-
59
-	protected function setUp(): void {
60
-		parent::setUp();
61
-
62
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
63
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_expire_after_n_days', '7');
64
-
65
-		Filesystem::getLoader()->removeStorageWrapper('sharing_mask');
66
-
67
-		$this->folder = self::TEST_FOLDER_NAME;
68
-		$this->subfolder = '/subfolder_share_api_test';
69
-		$this->subsubfolder = '/subsubfolder_share_api_test';
70
-
71
-		$this->filename = '/share-api-test.txt';
72
-
73
-		// save file with content
74
-		$this->view->file_put_contents($this->filename, $this->data);
75
-		$this->view->mkdir($this->folder);
76
-		$this->view->mkdir($this->folder . $this->subfolder);
77
-		$this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
78
-		$this->view->file_put_contents($this->folder . $this->filename, $this->data);
79
-		$this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data);
80
-		$mount = $this->view->getMount($this->filename);
81
-		$mount->getStorage()->getScanner()->scan('', Scanner::SCAN_RECURSIVE);
82
-
83
-		$this->userFolder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER1);
84
-	}
85
-
86
-	protected function tearDown(): void {
87
-		if ($this->view instanceof View) {
88
-			$this->view->unlink($this->filename);
89
-			$this->view->deleteAll($this->folder);
90
-		}
91
-
92
-		self::$tempStorage = null;
93
-
94
-		parent::tearDown();
95
-	}
96
-
97
-	/**
98
-	 * @param string $userId The userId of the caller
99
-	 * @return ShareAPIController
100
-	 */
101
-	private function createOCS($userId) {
102
-		$l = $this->getMockBuilder(IL10N::class)->getMock();
103
-		$l->method('t')
104
-			->willReturnCallback(function ($text, $parameters = []) {
105
-				return vsprintf($text, $parameters);
106
-			});
107
-		$config = $this->createMock(IConfig::class);
108
-		$appManager = $this->createMock(IAppManager::class);
109
-		$serverContainer = $this->createMock(ContainerInterface::class);
110
-		$userStatusManager = $this->createMock(IUserStatusManager::class);
111
-		$previewManager = $this->createMock(IPreview::class);
112
-		$dateTimeZone = $this->createMock(IDateTimeZone::class);
113
-		$logger = $this->createMock(LoggerInterface::class);
114
-		$providerFactory = $this->createMock(IProviderFactory::class);
115
-		$mailer = $this->createMock(IMailer::class);
116
-		$tagManager = $this->createMock(ITagManager::class);
117
-		$dateTimeZone->method('getTimeZone')->willReturn(new \DateTimeZone(date_default_timezone_get()));
118
-
119
-		return new ShareAPIController(
120
-			self::APP_NAME,
121
-			$this->getMockBuilder(IRequest::class)->getMock(),
122
-			$this->shareManager,
123
-			Server::get(IGroupManager::class),
124
-			Server::get(IUserManager::class),
125
-			Server::get(IRootFolder::class),
126
-			Server::get(IURLGenerator::class),
127
-			$l,
128
-			$config,
129
-			$appManager,
130
-			$serverContainer,
131
-			$userStatusManager,
132
-			$previewManager,
133
-			$dateTimeZone,
134
-			$logger,
135
-			$providerFactory,
136
-			$mailer,
137
-			$tagManager,
138
-			$userId,
139
-		);
140
-	}
141
-
142
-	public function testCreateShareUserFile(): void {
143
-		$this->setUp(); // for some reasons phpunit refuses to do this for us only for this test
144
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
145
-		$result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
146
-		$ocs->cleanup();
147
-
148
-		$data = $result->getData();
149
-		$this->assertEquals(19, $data['permissions']);
150
-		$this->assertEmpty($data['expiration']);
151
-
152
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
153
-
154
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
155
-		$ocs->deleteShare($data['id']);
156
-
157
-		$ocs->cleanup();
158
-	}
159
-
160
-	public function testCreateShareUserFolder(): void {
161
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
162
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
163
-		$ocs->cleanup();
164
-
165
-		$data = $result->getData();
166
-		$this->assertEquals(31, $data['permissions']);
167
-		$this->assertEmpty($data['expiration']);
168
-
169
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
170
-
171
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
172
-		$ocs->deleteShare($data['id']);
173
-		$ocs->cleanup();
174
-	}
175
-
176
-
177
-	public function testCreateShareGroupFile(): void {
178
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
179
-		$result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
180
-		$ocs->cleanup();
181
-
182
-		$data = $result->getData();
183
-		$this->assertEquals(19, $data['permissions']);
184
-		$this->assertEmpty($data['expiration']);
185
-
186
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
187
-
188
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
189
-		$ocs->deleteShare($data['id']);
190
-		$ocs->cleanup();
191
-	}
192
-
193
-	public function testCreateShareGroupFolder(): void {
194
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
195
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
196
-		$ocs->cleanup();
197
-
198
-		$data = $result->getData();
199
-		$this->assertEquals(31, $data['permissions']);
200
-		$this->assertEmpty($data['expiration']);
201
-
202
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
203
-
204
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
205
-		$ocs->deleteShare($data['id']);
206
-		$ocs->cleanup();
207
-	}
208
-
209
-	/**
210
-	 * @group RoutingWeirdness
211
-	 */
212
-	public function testCreateShareLink(): void {
213
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
214
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
215
-		$ocs->cleanup();
216
-
217
-		$data = $result->getData();
218
-		$this->assertEquals(Constants::PERMISSION_ALL,
219
-			$data['permissions']);
220
-		$this->assertEmpty($data['expiration']);
221
-		$this->assertTrue(is_string($data['token']));
222
-
223
-		// check for correct link
224
-		$url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
225
-		$this->assertEquals($url, $data['url']);
226
-
227
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
228
-
229
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
230
-		$ocs->deleteShare($data['id']);
231
-		$ocs->cleanup();
232
-	}
233
-
234
-	/**
235
-	 * @group RoutingWeirdness
236
-	 */
237
-	public function testCreateShareLinkPublicUpload(): void {
238
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
239
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
240
-		$ocs->cleanup();
241
-
242
-		$data = $result->getData();
243
-		$this->assertEquals(
244
-			Constants::PERMISSION_READ
245
-			| Constants::PERMISSION_CREATE
246
-			| Constants::PERMISSION_UPDATE
247
-			| Constants::PERMISSION_DELETE
248
-			| Constants::PERMISSION_SHARE,
249
-			$data['permissions']
250
-		);
251
-		$this->assertEmpty($data['expiration']);
252
-		$this->assertTrue(is_string($data['token']));
253
-
254
-		// check for correct link
255
-		$url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
256
-		$this->assertEquals($url, $data['url']);
257
-
258
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
259
-
260
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
261
-		$ocs->deleteShare($data['id']);
262
-		$ocs->cleanup();
263
-	}
264
-
265
-	public function testEnforceLinkPassword(): void {
266
-		$password = md5(time());
267
-		$config = Server::get(IConfig::class);
268
-		$config->setAppValue('core', 'shareapi_enforce_links_password', 'yes');
269
-
270
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
271
-		try {
272
-			$ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
273
-			$this->fail();
274
-		} catch (OCSForbiddenException $e) {
275
-		}
276
-		$ocs->cleanup();
277
-
278
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
279
-		try {
280
-			$ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '');
281
-			$this->fail();
282
-		} catch (OCSForbiddenException $e) {
283
-		}
284
-		$ocs->cleanup();
285
-
286
-		// share with password should succeed
287
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
288
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', $password);
289
-		$ocs->cleanup();
290
-
291
-		$data = $result->getData();
292
-
293
-		// setting new password should succeed
294
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
295
-		$ocs->updateShare($data['id'], null, $password);
296
-		$ocs->cleanup();
297
-
298
-		// removing password should fail
299
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
300
-		try {
301
-			$ocs->updateShare($data['id']);
302
-			$this->fail();
303
-		} catch (OCSBadRequestException $e) {
304
-		}
305
-		$ocs->cleanup();
306
-
307
-		// cleanup
308
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
309
-		$ocs->deleteShare($data['id']);
310
-		$ocs->cleanup();
311
-
312
-		$config->setAppValue('core', 'shareapi_enforce_links_password', 'no');
313
-		$this->addToAssertionCount(1);
314
-	}
315
-
316
-	/**
317
-	 * @medium
318
-	 */
319
-	public function testSharePermissions(): void {
320
-		// sharing file to a user should work if shareapi_exclude_groups is set
321
-		// to no
322
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
323
-
324
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
325
-		$result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
326
-		$ocs->cleanup();
327
-
328
-		$data = $result->getData();
329
-
330
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
331
-
332
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
333
-		$ocs->deleteShare($data['id']);
334
-		$ocs->cleanup();
335
-
336
-		// exclude groups, but not the group the user belongs to. Sharing should still work
337
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'yes');
338
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', 'admin,group1,group2');
339
-
340
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
341
-		$result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
342
-		$ocs->cleanup();
343
-
344
-		$data = $result->getData();
345
-
346
-		$this->shareManager->getShareById('ocinternal:' . $data['id']);
347
-
348
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
349
-		$ocs->deleteShare($data['id']);
350
-		$ocs->cleanup();
351
-
352
-		// now we exclude the group the user belongs to ('group'), sharing should fail now
353
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', 'admin,group');
354
-
355
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
356
-		$ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
357
-		$ocs->cleanup();
358
-
359
-		// cleanup
360
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
361
-		Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', '');
362
-
363
-		$this->addToAssertionCount(1);
364
-	}
365
-
366
-
367
-	/**
368
-	 * @medium
369
-	 */
370
-	public function testGetAllShares(): void {
371
-		$node = $this->userFolder->get($this->filename);
372
-
373
-		$share = $this->shareManager->newShare();
374
-		$share->setNode($node)
375
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
376
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
377
-			->setShareType(IShare::TYPE_USER)
378
-			->setPermissions(19);
379
-
380
-		$share = $this->shareManager->createShare($share);
381
-
382
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
383
-		$result = $ocs->getShares();
384
-		$ocs->cleanup();
385
-
386
-		$this->assertTrue(count($result->getData()) === 1);
387
-
388
-		$this->shareManager->deleteShare($share);
389
-	}
390
-
391
-	public function testGetAllSharesWithMe(): void {
392
-		$this->loginAsUser(self::TEST_FILES_SHARING_API_USER2);
393
-		$this->logout();
394
-
395
-		$node1 = $this->userFolder->get($this->filename);
396
-		$share1 = $this->shareManager->newShare();
397
-		$share1->setNode($node1)
398
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
399
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
400
-			->setShareType(IShare::TYPE_USER)
401
-			->setPermissions(19);
402
-		$share1 = $this->shareManager->createShare($share1);
403
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
404
-		$this->shareManager->updateShare($share1);
405
-
406
-		$node2 = $this->userFolder->get($this->folder);
407
-		$share2 = $this->shareManager->newShare();
408
-		$share2->setNode($node2)
409
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
410
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
411
-			->setShareType(IShare::TYPE_USER)
412
-			->setPermissions(31);
413
-		$share2 = $this->shareManager->createShare($share2);
414
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
415
-		$this->shareManager->updateShare($share2);
416
-
417
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
418
-		$result = $ocs->getShares('true');
419
-		$ocs->cleanup();
420
-
421
-		$this->assertCount(2, $result->getData());
422
-
423
-		$this->shareManager->deleteShare($share1);
424
-		$this->shareManager->deleteShare($share2);
425
-	}
426
-
427
-	/**
428
-	 * @medium
429
-	 * @group RoutingWeirdness
430
-	 */
431
-	public function testPublicLinkUrl(): void {
432
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
433
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
434
-		$ocs->cleanup();
435
-
436
-		$data = $result->getData();
437
-
438
-		// check if we have a token
439
-		$this->assertTrue(is_string($data['token']));
440
-		$id = $data['id'];
441
-
442
-		// check for correct link
443
-		$url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
444
-		$this->assertEquals($url, $data['url']);
445
-
446
-		// check for link in getall shares
447
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
448
-		$result = $ocs->getShares();
449
-		$ocs->cleanup();
450
-
451
-		$data = $result->getData();
452
-		$this->assertEquals($url, current($data)['url']);
453
-
454
-		// check for path
455
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
456
-		$result = $ocs->getShares();
457
-		$ocs->cleanup();
458
-
459
-		$data = $result->getData();
460
-		$this->assertEquals($url, current($data)['url']);
461
-
462
-		// check in share id
463
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
464
-		$result = $ocs->getShare($id);
465
-		$ocs->cleanup();
466
-
467
-		$data = $result->getData();
468
-		$this->assertEquals($url, current($data)['url']);
469
-
470
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
471
-		$ocs->deleteShare($id);
472
-		$ocs->cleanup();
473
-	}
474
-
475
-	/**
476
-	 * @medium
477
-	 * @depends testCreateShareUserFile
478
-	 * @depends testCreateShareLink
479
-	 */
480
-	public function testGetShareFromSource(): void {
481
-		$node = $this->userFolder->get($this->filename);
482
-		$share = $this->shareManager->newShare();
483
-		$share->setNode($node)
484
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
485
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
486
-			->setShareType(IShare::TYPE_USER)
487
-			->setPermissions(19);
488
-		$share1 = $this->shareManager->createShare($share);
489
-
490
-		$share = $this->shareManager->newShare();
491
-		$share->setNode($node)
492
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
493
-			->setShareType(IShare::TYPE_LINK)
494
-			->setPermissions(1);
495
-		$share2 = $this->shareManager->createShare($share);
496
-
497
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
498
-		$result = $ocs->getShares();
499
-		$ocs->cleanup();
500
-
501
-		// test should return one share created from testCreateShare()
502
-		$this->assertTrue(count($result->getData()) === 2);
503
-
504
-		$this->shareManager->deleteShare($share1);
505
-		$this->shareManager->deleteShare($share2);
506
-	}
507
-
508
-	/**
509
-	 * @medium
510
-	 * @depends testCreateShareUserFile
511
-	 * @depends testCreateShareLink
512
-	 */
513
-	public function testGetShareFromSourceWithReshares(): void {
514
-		$node = $this->userFolder->get($this->filename);
515
-		$share1 = $this->shareManager->newShare();
516
-		$share1->setNode($node)
517
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
518
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
519
-			->setShareType(IShare::TYPE_USER)
520
-			->setPermissions(19);
521
-		$share1 = $this->shareManager->createShare($share1);
522
-
523
-		$share2 = $this->shareManager->newShare();
524
-		$share2->setNode($node)
525
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
526
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
527
-			->setShareType(IShare::TYPE_USER)
528
-			->setPermissions(19);
529
-		$share2 = $this->shareManager->createShare($share2);
530
-
531
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
532
-		$result = $ocs->getShares();
533
-		$ocs->cleanup();
534
-
535
-		// test should return one share
536
-		$this->assertTrue(count($result->getData()) === 1);
537
-
538
-		// now also ask for the reshares
539
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
540
-		$result = $ocs->getShares('false', 'true', 'false', $this->filename);
541
-		$ocs->cleanup();
542
-
543
-		// now we should get two shares, the initial share and the reshare
544
-		$this->assertCount(2, $result->getData());
545
-
546
-		$this->shareManager->deleteShare($share1);
547
-		$this->shareManager->deleteShare($share2);
548
-	}
549
-
550
-	/**
551
-	 * @medium
552
-	 * @depends testCreateShareUserFile
553
-	 */
554
-	public function testGetShareFromId(): void {
555
-		$node = $this->userFolder->get($this->filename);
556
-		$share1 = $this->shareManager->newShare();
557
-		$share1->setNode($node)
558
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
559
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
560
-			->setShareType(IShare::TYPE_USER)
561
-			->setPermissions(19);
562
-		$share1 = $this->shareManager->createShare($share1);
563
-
564
-		// call getShare() with share ID
565
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
566
-		$result = $ocs->getShare($share1->getId());
567
-		$ocs->cleanup();
568
-
569
-		// test should return one share created from testCreateShare()
570
-		$this->assertEquals(1, count($result->getData()));
571
-
572
-		$this->shareManager->deleteShare($share1);
573
-	}
574
-
575
-	/**
576
-	 * @medium
577
-	 */
578
-	public function testGetShareFromFolder(): void {
579
-		$node1 = $this->userFolder->get($this->filename);
580
-		$share1 = $this->shareManager->newShare();
581
-		$share1->setNode($node1)
582
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
583
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
584
-			->setShareType(IShare::TYPE_USER)
585
-			->setPermissions(19);
586
-		$share1 = $this->shareManager->createShare($share1);
587
-
588
-		$node2 = $this->userFolder->get($this->folder . '/' . $this->filename);
589
-		$share2 = $this->shareManager->newShare();
590
-		$share2->setNode($node2)
591
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
592
-			->setShareType(IShare::TYPE_LINK)
593
-			->setPermissions(1);
594
-		$share2 = $this->shareManager->createShare($share2);
595
-
596
-
597
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
598
-		$result = $ocs->getShares('false', 'false', 'true', $this->folder);
599
-		$ocs->cleanup();
600
-
601
-		// test should return one share within $this->folder
602
-		$this->assertTrue(count($result->getData()) === 1);
603
-
604
-		$this->shareManager->deleteShare($share1);
605
-		$this->shareManager->deleteShare($share2);
606
-	}
607
-
608
-	public function testGetShareFromFolderWithFile(): void {
609
-		$node1 = $this->userFolder->get($this->filename);
610
-		$share1 = $this->shareManager->newShare();
611
-		$share1->setNode($node1)
612
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
613
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
614
-			->setShareType(IShare::TYPE_USER)
615
-			->setPermissions(19);
616
-		$share1 = $this->shareManager->createShare($share1);
617
-
618
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
619
-		try {
620
-			$ocs->getShares('false', 'false', 'true', $this->filename);
621
-			$this->fail();
622
-		} catch (OCSBadRequestException $e) {
623
-			$this->assertEquals('Not a directory', $e->getMessage());
624
-		}
625
-		$ocs->cleanup();
626
-
627
-		$this->shareManager->deleteShare($share1);
628
-	}
629
-
630
-	/**
631
-	 * share a folder, than reshare a file within the shared folder and check if we construct the correct path
632
-	 * @medium
633
-	 */
634
-	public function testGetShareFromFolderReshares(): void {
635
-		$node1 = $this->userFolder->get($this->folder);
636
-		$share1 = $this->shareManager->newShare();
637
-		$share1->setNode($node1)
638
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
639
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
640
-			->setShareType(IShare::TYPE_USER)
641
-			->setPermissions(31);
642
-		$share1 = $this->shareManager->createShare($share1);
643
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
644
-		$this->shareManager->updateShare($share1);
645
-
646
-		$node2 = $this->userFolder->get($this->folder . '/' . $this->filename);
647
-		$share2 = $this->shareManager->newShare();
648
-		$share2->setNode($node2)
649
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
650
-			->setShareType(IShare::TYPE_LINK)
651
-			->setPermissions(1);
652
-		$share2 = $this->shareManager->createShare($share2);
653
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
654
-		$this->shareManager->updateShare($share2);
655
-
656
-		$node3 = $this->userFolder->get($this->folder . '/' . $this->subfolder . '/' . $this->filename);
657
-		$share3 = $this->shareManager->newShare();
658
-		$share3->setNode($node3)
659
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
660
-			->setShareType(IShare::TYPE_LINK)
661
-			->setPermissions(1);
662
-		$share3 = $this->shareManager->createShare($share3);
663
-		$share3->setStatus(IShare::STATUS_ACCEPTED);
664
-		$this->shareManager->updateShare($share3);
665
-
666
-		$testValues = [
667
-			['query' => $this->folder,
668
-				'expectedResult' => $this->folder . $this->filename],
669
-			['query' => $this->folder . $this->subfolder,
670
-				'expectedResult' => $this->folder . $this->subfolder . $this->filename],
671
-		];
672
-
673
-		foreach ($testValues as $value) {
674
-			$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
675
-			$result = $ocs->getShares('false', 'false', 'true', $value['query']);
676
-			$ocs->cleanup();
677
-
678
-			// test should return one share within $this->folder
679
-			$data = $result->getData();
680
-
681
-			$this->assertEquals($value['expectedResult'], $data[0]['path']);
682
-		}
683
-
684
-		// cleanup
685
-		$this->shareManager->deleteShare($share1);
686
-		$this->shareManager->deleteShare($share2);
687
-		$this->shareManager->deleteShare($share3);
688
-	}
689
-
690
-	/**
691
-	 * reshare a sub folder and check if we get the correct path
692
-	 * @medium
693
-	 */
694
-	public function testGetShareFromSubFolderReShares(): void {
695
-		$node1 = $this->userFolder->get($this->folder . $this->subfolder);
696
-		$share1 = $this->shareManager->newShare();
697
-		$share1->setNode($node1)
698
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
699
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
700
-			->setShareType(IShare::TYPE_USER)
701
-			->setPermissions(31);
702
-		$share1 = $this->shareManager->createShare($share1);
703
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
704
-		$this->shareManager->updateShare($share1);
705
-
706
-		$node2 = Server::get(IRootFolder::class)->getUserFolder(self::TEST_FILES_SHARING_API_USER2)->get($this->subfolder);
707
-		$share2 = $this->shareManager->newShare();
708
-		$share2->setNode($node2)
709
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
710
-			->setShareType(IShare::TYPE_LINK)
711
-			->setPermissions(1);
712
-		$share2 = $this->shareManager->createShare($share2);
713
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
714
-		$this->shareManager->updateShare($share2);
715
-
716
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
717
-		$result = $ocs->getShares();
718
-		$ocs->cleanup();
719
-
720
-		// test should return one share within $this->folder
721
-		$data = $result->getData();
722
-
723
-		// we should get exactly one result
724
-		$this->assertCount(1, $data);
725
-
726
-		$this->assertEquals($this->subfolder, $data[0]['path']);
727
-
728
-		$this->shareManager->deleteShare($share2);
729
-		$this->shareManager->deleteShare($share1);
730
-	}
731
-
732
-	/**
733
-	 * test re-re-share of folder if the path gets constructed correctly
734
-	 * @medium
735
-	 */
736
-	public function XtestGetShareFromFolderReReShares() {
737
-		$node1 = $this->userFolder->get($this->folder . $this->subfolder);
738
-		$share1 = $this->shareManager->newShare();
739
-		$share1->setNode($node1)
740
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
741
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
742
-			->setShareType(IShare::TYPE_USER)
743
-			->setPermissions(31);
744
-		$share1 = $this->shareManager->createShare($share1);
745
-
746
-		$node2 = $this->userFolder->get($this->folder . $this->subfolder . $this->subsubfolder);
747
-		$share2 = $this->shareManager->newShare();
748
-		$share2->setNode($node2)
749
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
750
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
751
-			->setShareType(IShare::TYPE_USER)
752
-			->setPermissions(31);
753
-		$share2 = $this->shareManager->createShare($share2);
754
-
755
-		$share3 = $this->shareManager->newShare();
756
-		$share3->setNode($node2)
757
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER3)
758
-			->setShareType(IShare::TYPE_LINK)
759
-			->setPermissions(1);
760
-		$share3 = $this->shareManager->createShare($share3);
761
-
762
-		/*
48
+    public const TEST_FOLDER_NAME = '/folder_share_api_test';
49
+    public const APP_NAME = 'files_sharing';
50
+
51
+    private static $tempStorage;
52
+
53
+    /** @var Folder */
54
+    private $userFolder;
55
+
56
+    /** @var string */
57
+    private $subsubfolder;
58
+
59
+    protected function setUp(): void {
60
+        parent::setUp();
61
+
62
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
63
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_expire_after_n_days', '7');
64
+
65
+        Filesystem::getLoader()->removeStorageWrapper('sharing_mask');
66
+
67
+        $this->folder = self::TEST_FOLDER_NAME;
68
+        $this->subfolder = '/subfolder_share_api_test';
69
+        $this->subsubfolder = '/subsubfolder_share_api_test';
70
+
71
+        $this->filename = '/share-api-test.txt';
72
+
73
+        // save file with content
74
+        $this->view->file_put_contents($this->filename, $this->data);
75
+        $this->view->mkdir($this->folder);
76
+        $this->view->mkdir($this->folder . $this->subfolder);
77
+        $this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
78
+        $this->view->file_put_contents($this->folder . $this->filename, $this->data);
79
+        $this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data);
80
+        $mount = $this->view->getMount($this->filename);
81
+        $mount->getStorage()->getScanner()->scan('', Scanner::SCAN_RECURSIVE);
82
+
83
+        $this->userFolder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER1);
84
+    }
85
+
86
+    protected function tearDown(): void {
87
+        if ($this->view instanceof View) {
88
+            $this->view->unlink($this->filename);
89
+            $this->view->deleteAll($this->folder);
90
+        }
91
+
92
+        self::$tempStorage = null;
93
+
94
+        parent::tearDown();
95
+    }
96
+
97
+    /**
98
+     * @param string $userId The userId of the caller
99
+     * @return ShareAPIController
100
+     */
101
+    private function createOCS($userId) {
102
+        $l = $this->getMockBuilder(IL10N::class)->getMock();
103
+        $l->method('t')
104
+            ->willReturnCallback(function ($text, $parameters = []) {
105
+                return vsprintf($text, $parameters);
106
+            });
107
+        $config = $this->createMock(IConfig::class);
108
+        $appManager = $this->createMock(IAppManager::class);
109
+        $serverContainer = $this->createMock(ContainerInterface::class);
110
+        $userStatusManager = $this->createMock(IUserStatusManager::class);
111
+        $previewManager = $this->createMock(IPreview::class);
112
+        $dateTimeZone = $this->createMock(IDateTimeZone::class);
113
+        $logger = $this->createMock(LoggerInterface::class);
114
+        $providerFactory = $this->createMock(IProviderFactory::class);
115
+        $mailer = $this->createMock(IMailer::class);
116
+        $tagManager = $this->createMock(ITagManager::class);
117
+        $dateTimeZone->method('getTimeZone')->willReturn(new \DateTimeZone(date_default_timezone_get()));
118
+
119
+        return new ShareAPIController(
120
+            self::APP_NAME,
121
+            $this->getMockBuilder(IRequest::class)->getMock(),
122
+            $this->shareManager,
123
+            Server::get(IGroupManager::class),
124
+            Server::get(IUserManager::class),
125
+            Server::get(IRootFolder::class),
126
+            Server::get(IURLGenerator::class),
127
+            $l,
128
+            $config,
129
+            $appManager,
130
+            $serverContainer,
131
+            $userStatusManager,
132
+            $previewManager,
133
+            $dateTimeZone,
134
+            $logger,
135
+            $providerFactory,
136
+            $mailer,
137
+            $tagManager,
138
+            $userId,
139
+        );
140
+    }
141
+
142
+    public function testCreateShareUserFile(): void {
143
+        $this->setUp(); // for some reasons phpunit refuses to do this for us only for this test
144
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
145
+        $result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
146
+        $ocs->cleanup();
147
+
148
+        $data = $result->getData();
149
+        $this->assertEquals(19, $data['permissions']);
150
+        $this->assertEmpty($data['expiration']);
151
+
152
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
153
+
154
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
155
+        $ocs->deleteShare($data['id']);
156
+
157
+        $ocs->cleanup();
158
+    }
159
+
160
+    public function testCreateShareUserFolder(): void {
161
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
162
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
163
+        $ocs->cleanup();
164
+
165
+        $data = $result->getData();
166
+        $this->assertEquals(31, $data['permissions']);
167
+        $this->assertEmpty($data['expiration']);
168
+
169
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
170
+
171
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
172
+        $ocs->deleteShare($data['id']);
173
+        $ocs->cleanup();
174
+    }
175
+
176
+
177
+    public function testCreateShareGroupFile(): void {
178
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
179
+        $result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
180
+        $ocs->cleanup();
181
+
182
+        $data = $result->getData();
183
+        $this->assertEquals(19, $data['permissions']);
184
+        $this->assertEmpty($data['expiration']);
185
+
186
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
187
+
188
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
189
+        $ocs->deleteShare($data['id']);
190
+        $ocs->cleanup();
191
+    }
192
+
193
+    public function testCreateShareGroupFolder(): void {
194
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
195
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
196
+        $ocs->cleanup();
197
+
198
+        $data = $result->getData();
199
+        $this->assertEquals(31, $data['permissions']);
200
+        $this->assertEmpty($data['expiration']);
201
+
202
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
203
+
204
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
205
+        $ocs->deleteShare($data['id']);
206
+        $ocs->cleanup();
207
+    }
208
+
209
+    /**
210
+     * @group RoutingWeirdness
211
+     */
212
+    public function testCreateShareLink(): void {
213
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
214
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
215
+        $ocs->cleanup();
216
+
217
+        $data = $result->getData();
218
+        $this->assertEquals(Constants::PERMISSION_ALL,
219
+            $data['permissions']);
220
+        $this->assertEmpty($data['expiration']);
221
+        $this->assertTrue(is_string($data['token']));
222
+
223
+        // check for correct link
224
+        $url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
225
+        $this->assertEquals($url, $data['url']);
226
+
227
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
228
+
229
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
230
+        $ocs->deleteShare($data['id']);
231
+        $ocs->cleanup();
232
+    }
233
+
234
+    /**
235
+     * @group RoutingWeirdness
236
+     */
237
+    public function testCreateShareLinkPublicUpload(): void {
238
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
239
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'true');
240
+        $ocs->cleanup();
241
+
242
+        $data = $result->getData();
243
+        $this->assertEquals(
244
+            Constants::PERMISSION_READ
245
+            | Constants::PERMISSION_CREATE
246
+            | Constants::PERMISSION_UPDATE
247
+            | Constants::PERMISSION_DELETE
248
+            | Constants::PERMISSION_SHARE,
249
+            $data['permissions']
250
+        );
251
+        $this->assertEmpty($data['expiration']);
252
+        $this->assertTrue(is_string($data['token']));
253
+
254
+        // check for correct link
255
+        $url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
256
+        $this->assertEquals($url, $data['url']);
257
+
258
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
259
+
260
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
261
+        $ocs->deleteShare($data['id']);
262
+        $ocs->cleanup();
263
+    }
264
+
265
+    public function testEnforceLinkPassword(): void {
266
+        $password = md5(time());
267
+        $config = Server::get(IConfig::class);
268
+        $config->setAppValue('core', 'shareapi_enforce_links_password', 'yes');
269
+
270
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
271
+        try {
272
+            $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
273
+            $this->fail();
274
+        } catch (OCSForbiddenException $e) {
275
+        }
276
+        $ocs->cleanup();
277
+
278
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
279
+        try {
280
+            $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '');
281
+            $this->fail();
282
+        } catch (OCSForbiddenException $e) {
283
+        }
284
+        $ocs->cleanup();
285
+
286
+        // share with password should succeed
287
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
288
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', $password);
289
+        $ocs->cleanup();
290
+
291
+        $data = $result->getData();
292
+
293
+        // setting new password should succeed
294
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
295
+        $ocs->updateShare($data['id'], null, $password);
296
+        $ocs->cleanup();
297
+
298
+        // removing password should fail
299
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
300
+        try {
301
+            $ocs->updateShare($data['id']);
302
+            $this->fail();
303
+        } catch (OCSBadRequestException $e) {
304
+        }
305
+        $ocs->cleanup();
306
+
307
+        // cleanup
308
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
309
+        $ocs->deleteShare($data['id']);
310
+        $ocs->cleanup();
311
+
312
+        $config->setAppValue('core', 'shareapi_enforce_links_password', 'no');
313
+        $this->addToAssertionCount(1);
314
+    }
315
+
316
+    /**
317
+     * @medium
318
+     */
319
+    public function testSharePermissions(): void {
320
+        // sharing file to a user should work if shareapi_exclude_groups is set
321
+        // to no
322
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
323
+
324
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
325
+        $result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
326
+        $ocs->cleanup();
327
+
328
+        $data = $result->getData();
329
+
330
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
331
+
332
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
333
+        $ocs->deleteShare($data['id']);
334
+        $ocs->cleanup();
335
+
336
+        // exclude groups, but not the group the user belongs to. Sharing should still work
337
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'yes');
338
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', 'admin,group1,group2');
339
+
340
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
341
+        $result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
342
+        $ocs->cleanup();
343
+
344
+        $data = $result->getData();
345
+
346
+        $this->shareManager->getShareById('ocinternal:' . $data['id']);
347
+
348
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
349
+        $ocs->deleteShare($data['id']);
350
+        $ocs->cleanup();
351
+
352
+        // now we exclude the group the user belongs to ('group'), sharing should fail now
353
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', 'admin,group');
354
+
355
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
356
+        $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
357
+        $ocs->cleanup();
358
+
359
+        // cleanup
360
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups', 'no');
361
+        Server::get(IConfig::class)->setAppValue('core', 'shareapi_exclude_groups_list', '');
362
+
363
+        $this->addToAssertionCount(1);
364
+    }
365
+
366
+
367
+    /**
368
+     * @medium
369
+     */
370
+    public function testGetAllShares(): void {
371
+        $node = $this->userFolder->get($this->filename);
372
+
373
+        $share = $this->shareManager->newShare();
374
+        $share->setNode($node)
375
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
376
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
377
+            ->setShareType(IShare::TYPE_USER)
378
+            ->setPermissions(19);
379
+
380
+        $share = $this->shareManager->createShare($share);
381
+
382
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
383
+        $result = $ocs->getShares();
384
+        $ocs->cleanup();
385
+
386
+        $this->assertTrue(count($result->getData()) === 1);
387
+
388
+        $this->shareManager->deleteShare($share);
389
+    }
390
+
391
+    public function testGetAllSharesWithMe(): void {
392
+        $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2);
393
+        $this->logout();
394
+
395
+        $node1 = $this->userFolder->get($this->filename);
396
+        $share1 = $this->shareManager->newShare();
397
+        $share1->setNode($node1)
398
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
399
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
400
+            ->setShareType(IShare::TYPE_USER)
401
+            ->setPermissions(19);
402
+        $share1 = $this->shareManager->createShare($share1);
403
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
404
+        $this->shareManager->updateShare($share1);
405
+
406
+        $node2 = $this->userFolder->get($this->folder);
407
+        $share2 = $this->shareManager->newShare();
408
+        $share2->setNode($node2)
409
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
410
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
411
+            ->setShareType(IShare::TYPE_USER)
412
+            ->setPermissions(31);
413
+        $share2 = $this->shareManager->createShare($share2);
414
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
415
+        $this->shareManager->updateShare($share2);
416
+
417
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
418
+        $result = $ocs->getShares('true');
419
+        $ocs->cleanup();
420
+
421
+        $this->assertCount(2, $result->getData());
422
+
423
+        $this->shareManager->deleteShare($share1);
424
+        $this->shareManager->deleteShare($share2);
425
+    }
426
+
427
+    /**
428
+     * @medium
429
+     * @group RoutingWeirdness
430
+     */
431
+    public function testPublicLinkUrl(): void {
432
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
433
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
434
+        $ocs->cleanup();
435
+
436
+        $data = $result->getData();
437
+
438
+        // check if we have a token
439
+        $this->assertTrue(is_string($data['token']));
440
+        $id = $data['id'];
441
+
442
+        // check for correct link
443
+        $url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
444
+        $this->assertEquals($url, $data['url']);
445
+
446
+        // check for link in getall shares
447
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
448
+        $result = $ocs->getShares();
449
+        $ocs->cleanup();
450
+
451
+        $data = $result->getData();
452
+        $this->assertEquals($url, current($data)['url']);
453
+
454
+        // check for path
455
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
456
+        $result = $ocs->getShares();
457
+        $ocs->cleanup();
458
+
459
+        $data = $result->getData();
460
+        $this->assertEquals($url, current($data)['url']);
461
+
462
+        // check in share id
463
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
464
+        $result = $ocs->getShare($id);
465
+        $ocs->cleanup();
466
+
467
+        $data = $result->getData();
468
+        $this->assertEquals($url, current($data)['url']);
469
+
470
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
471
+        $ocs->deleteShare($id);
472
+        $ocs->cleanup();
473
+    }
474
+
475
+    /**
476
+     * @medium
477
+     * @depends testCreateShareUserFile
478
+     * @depends testCreateShareLink
479
+     */
480
+    public function testGetShareFromSource(): void {
481
+        $node = $this->userFolder->get($this->filename);
482
+        $share = $this->shareManager->newShare();
483
+        $share->setNode($node)
484
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
485
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
486
+            ->setShareType(IShare::TYPE_USER)
487
+            ->setPermissions(19);
488
+        $share1 = $this->shareManager->createShare($share);
489
+
490
+        $share = $this->shareManager->newShare();
491
+        $share->setNode($node)
492
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
493
+            ->setShareType(IShare::TYPE_LINK)
494
+            ->setPermissions(1);
495
+        $share2 = $this->shareManager->createShare($share);
496
+
497
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
498
+        $result = $ocs->getShares();
499
+        $ocs->cleanup();
500
+
501
+        // test should return one share created from testCreateShare()
502
+        $this->assertTrue(count($result->getData()) === 2);
503
+
504
+        $this->shareManager->deleteShare($share1);
505
+        $this->shareManager->deleteShare($share2);
506
+    }
507
+
508
+    /**
509
+     * @medium
510
+     * @depends testCreateShareUserFile
511
+     * @depends testCreateShareLink
512
+     */
513
+    public function testGetShareFromSourceWithReshares(): void {
514
+        $node = $this->userFolder->get($this->filename);
515
+        $share1 = $this->shareManager->newShare();
516
+        $share1->setNode($node)
517
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
518
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
519
+            ->setShareType(IShare::TYPE_USER)
520
+            ->setPermissions(19);
521
+        $share1 = $this->shareManager->createShare($share1);
522
+
523
+        $share2 = $this->shareManager->newShare();
524
+        $share2->setNode($node)
525
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
526
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
527
+            ->setShareType(IShare::TYPE_USER)
528
+            ->setPermissions(19);
529
+        $share2 = $this->shareManager->createShare($share2);
530
+
531
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
532
+        $result = $ocs->getShares();
533
+        $ocs->cleanup();
534
+
535
+        // test should return one share
536
+        $this->assertTrue(count($result->getData()) === 1);
537
+
538
+        // now also ask for the reshares
539
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
540
+        $result = $ocs->getShares('false', 'true', 'false', $this->filename);
541
+        $ocs->cleanup();
542
+
543
+        // now we should get two shares, the initial share and the reshare
544
+        $this->assertCount(2, $result->getData());
545
+
546
+        $this->shareManager->deleteShare($share1);
547
+        $this->shareManager->deleteShare($share2);
548
+    }
549
+
550
+    /**
551
+     * @medium
552
+     * @depends testCreateShareUserFile
553
+     */
554
+    public function testGetShareFromId(): void {
555
+        $node = $this->userFolder->get($this->filename);
556
+        $share1 = $this->shareManager->newShare();
557
+        $share1->setNode($node)
558
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
559
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
560
+            ->setShareType(IShare::TYPE_USER)
561
+            ->setPermissions(19);
562
+        $share1 = $this->shareManager->createShare($share1);
563
+
564
+        // call getShare() with share ID
565
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
566
+        $result = $ocs->getShare($share1->getId());
567
+        $ocs->cleanup();
568
+
569
+        // test should return one share created from testCreateShare()
570
+        $this->assertEquals(1, count($result->getData()));
571
+
572
+        $this->shareManager->deleteShare($share1);
573
+    }
574
+
575
+    /**
576
+     * @medium
577
+     */
578
+    public function testGetShareFromFolder(): void {
579
+        $node1 = $this->userFolder->get($this->filename);
580
+        $share1 = $this->shareManager->newShare();
581
+        $share1->setNode($node1)
582
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
583
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
584
+            ->setShareType(IShare::TYPE_USER)
585
+            ->setPermissions(19);
586
+        $share1 = $this->shareManager->createShare($share1);
587
+
588
+        $node2 = $this->userFolder->get($this->folder . '/' . $this->filename);
589
+        $share2 = $this->shareManager->newShare();
590
+        $share2->setNode($node2)
591
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
592
+            ->setShareType(IShare::TYPE_LINK)
593
+            ->setPermissions(1);
594
+        $share2 = $this->shareManager->createShare($share2);
595
+
596
+
597
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
598
+        $result = $ocs->getShares('false', 'false', 'true', $this->folder);
599
+        $ocs->cleanup();
600
+
601
+        // test should return one share within $this->folder
602
+        $this->assertTrue(count($result->getData()) === 1);
603
+
604
+        $this->shareManager->deleteShare($share1);
605
+        $this->shareManager->deleteShare($share2);
606
+    }
607
+
608
+    public function testGetShareFromFolderWithFile(): void {
609
+        $node1 = $this->userFolder->get($this->filename);
610
+        $share1 = $this->shareManager->newShare();
611
+        $share1->setNode($node1)
612
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
613
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
614
+            ->setShareType(IShare::TYPE_USER)
615
+            ->setPermissions(19);
616
+        $share1 = $this->shareManager->createShare($share1);
617
+
618
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
619
+        try {
620
+            $ocs->getShares('false', 'false', 'true', $this->filename);
621
+            $this->fail();
622
+        } catch (OCSBadRequestException $e) {
623
+            $this->assertEquals('Not a directory', $e->getMessage());
624
+        }
625
+        $ocs->cleanup();
626
+
627
+        $this->shareManager->deleteShare($share1);
628
+    }
629
+
630
+    /**
631
+     * share a folder, than reshare a file within the shared folder and check if we construct the correct path
632
+     * @medium
633
+     */
634
+    public function testGetShareFromFolderReshares(): void {
635
+        $node1 = $this->userFolder->get($this->folder);
636
+        $share1 = $this->shareManager->newShare();
637
+        $share1->setNode($node1)
638
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
639
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
640
+            ->setShareType(IShare::TYPE_USER)
641
+            ->setPermissions(31);
642
+        $share1 = $this->shareManager->createShare($share1);
643
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
644
+        $this->shareManager->updateShare($share1);
645
+
646
+        $node2 = $this->userFolder->get($this->folder . '/' . $this->filename);
647
+        $share2 = $this->shareManager->newShare();
648
+        $share2->setNode($node2)
649
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
650
+            ->setShareType(IShare::TYPE_LINK)
651
+            ->setPermissions(1);
652
+        $share2 = $this->shareManager->createShare($share2);
653
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
654
+        $this->shareManager->updateShare($share2);
655
+
656
+        $node3 = $this->userFolder->get($this->folder . '/' . $this->subfolder . '/' . $this->filename);
657
+        $share3 = $this->shareManager->newShare();
658
+        $share3->setNode($node3)
659
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
660
+            ->setShareType(IShare::TYPE_LINK)
661
+            ->setPermissions(1);
662
+        $share3 = $this->shareManager->createShare($share3);
663
+        $share3->setStatus(IShare::STATUS_ACCEPTED);
664
+        $this->shareManager->updateShare($share3);
665
+
666
+        $testValues = [
667
+            ['query' => $this->folder,
668
+                'expectedResult' => $this->folder . $this->filename],
669
+            ['query' => $this->folder . $this->subfolder,
670
+                'expectedResult' => $this->folder . $this->subfolder . $this->filename],
671
+        ];
672
+
673
+        foreach ($testValues as $value) {
674
+            $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
675
+            $result = $ocs->getShares('false', 'false', 'true', $value['query']);
676
+            $ocs->cleanup();
677
+
678
+            // test should return one share within $this->folder
679
+            $data = $result->getData();
680
+
681
+            $this->assertEquals($value['expectedResult'], $data[0]['path']);
682
+        }
683
+
684
+        // cleanup
685
+        $this->shareManager->deleteShare($share1);
686
+        $this->shareManager->deleteShare($share2);
687
+        $this->shareManager->deleteShare($share3);
688
+    }
689
+
690
+    /**
691
+     * reshare a sub folder and check if we get the correct path
692
+     * @medium
693
+     */
694
+    public function testGetShareFromSubFolderReShares(): void {
695
+        $node1 = $this->userFolder->get($this->folder . $this->subfolder);
696
+        $share1 = $this->shareManager->newShare();
697
+        $share1->setNode($node1)
698
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
699
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
700
+            ->setShareType(IShare::TYPE_USER)
701
+            ->setPermissions(31);
702
+        $share1 = $this->shareManager->createShare($share1);
703
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
704
+        $this->shareManager->updateShare($share1);
705
+
706
+        $node2 = Server::get(IRootFolder::class)->getUserFolder(self::TEST_FILES_SHARING_API_USER2)->get($this->subfolder);
707
+        $share2 = $this->shareManager->newShare();
708
+        $share2->setNode($node2)
709
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
710
+            ->setShareType(IShare::TYPE_LINK)
711
+            ->setPermissions(1);
712
+        $share2 = $this->shareManager->createShare($share2);
713
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
714
+        $this->shareManager->updateShare($share2);
715
+
716
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
717
+        $result = $ocs->getShares();
718
+        $ocs->cleanup();
719
+
720
+        // test should return one share within $this->folder
721
+        $data = $result->getData();
722
+
723
+        // we should get exactly one result
724
+        $this->assertCount(1, $data);
725
+
726
+        $this->assertEquals($this->subfolder, $data[0]['path']);
727
+
728
+        $this->shareManager->deleteShare($share2);
729
+        $this->shareManager->deleteShare($share1);
730
+    }
731
+
732
+    /**
733
+     * test re-re-share of folder if the path gets constructed correctly
734
+     * @medium
735
+     */
736
+    public function XtestGetShareFromFolderReReShares() {
737
+        $node1 = $this->userFolder->get($this->folder . $this->subfolder);
738
+        $share1 = $this->shareManager->newShare();
739
+        $share1->setNode($node1)
740
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
741
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
742
+            ->setShareType(IShare::TYPE_USER)
743
+            ->setPermissions(31);
744
+        $share1 = $this->shareManager->createShare($share1);
745
+
746
+        $node2 = $this->userFolder->get($this->folder . $this->subfolder . $this->subsubfolder);
747
+        $share2 = $this->shareManager->newShare();
748
+        $share2->setNode($node2)
749
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
750
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
751
+            ->setShareType(IShare::TYPE_USER)
752
+            ->setPermissions(31);
753
+        $share2 = $this->shareManager->createShare($share2);
754
+
755
+        $share3 = $this->shareManager->newShare();
756
+        $share3->setNode($node2)
757
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER3)
758
+            ->setShareType(IShare::TYPE_LINK)
759
+            ->setPermissions(1);
760
+        $share3 = $this->shareManager->createShare($share3);
761
+
762
+        /*
763 763
 		 * Test as recipient
764 764
 		 */
765
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
766
-		$result = $ocs->getShares();
767
-		$ocs->cleanup();
765
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
766
+        $result = $ocs->getShares();
767
+        $ocs->cleanup();
768 768
 
769
-		// test should return one share within $this->folder
770
-		$data = $result->getData();
769
+        // test should return one share within $this->folder
770
+        $data = $result->getData();
771 771
 
772
-		// we should get exactly one result
773
-		$this->assertCount(1, $data);
774
-		$this->assertEquals($this->subsubfolder, $data[0]['path']);
772
+        // we should get exactly one result
773
+        $this->assertCount(1, $data);
774
+        $this->assertEquals($this->subsubfolder, $data[0]['path']);
775 775
 
776
-		/*
776
+        /*
777 777
 		 * Test for first owner/initiator
778 778
 		 */
779
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
780
-		$result = $ocs->getShares();
781
-		$ocs->cleanup();
779
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
780
+        $result = $ocs->getShares();
781
+        $ocs->cleanup();
782 782
 
783
-		// test should return one share within $this->folder
784
-		$data = $result->getData();
783
+        // test should return one share within $this->folder
784
+        $data = $result->getData();
785 785
 
786
-		// we should get exactly one result
787
-		$this->assertCount(1, $data);
788
-		$this->assertEquals($this->folder . $this->subfolder, $data[0]['path']);
786
+        // we should get exactly one result
787
+        $this->assertCount(1, $data);
788
+        $this->assertEquals($this->folder . $this->subfolder, $data[0]['path']);
789 789
 
790
-		/*
790
+        /*
791 791
 		 * Test for second initiator
792 792
 		 */
793
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
794
-		$result = $ocs->getShares();
795
-		$ocs->cleanup();
796
-
797
-		// test should return one share within $this->folder
798
-		$data = $result->getData();
799
-
800
-		// we should get exactly one result
801
-		$this->assertCount(1, $data);
802
-		$this->assertEquals($this->subfolder . $this->subsubfolder, $data[0]['path']);
803
-
804
-		$this->shareManager->deleteShare($share1);
805
-		$this->shareManager->deleteShare($share2);
806
-		$this->shareManager->deleteShare($share3);
807
-	}
808
-
809
-	/**
810
-	 * test multiple shared folder if the path gets constructed correctly
811
-	 * @medium
812
-	 */
813
-	public function testGetShareMultipleSharedFolder(): void {
814
-		$this->setUp();
815
-		$node1 = $this->userFolder->get($this->folder . $this->subfolder);
816
-		$share1 = $this->shareManager->newShare();
817
-		$share1->setNode($node1)
818
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
819
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
820
-			->setShareType(IShare::TYPE_USER)
821
-			->setPermissions(31);
822
-		$share1 = $this->shareManager->createShare($share1);
823
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
824
-		$this->shareManager->updateShare($share1);
825
-
826
-		$node2 = $this->userFolder->get($this->folder);
827
-		$share2 = $this->shareManager->newShare();
828
-		$share2->setNode($node2)
829
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
830
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
831
-			->setShareType(IShare::TYPE_USER)
832
-			->setPermissions(31);
833
-		$share2 = $this->shareManager->createShare($share2);
834
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
835
-		$this->shareManager->updateShare($share2);
836
-
837
-		$share3 = $this->shareManager->newShare();
838
-		$share3->setNode($node1)
839
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
840
-			->setShareType(IShare::TYPE_LINK)
841
-			->setPermissions(1);
842
-		$share3 = $this->shareManager->createShare($share3);
843
-		$share3->setStatus(IShare::STATUS_ACCEPTED);
844
-		$this->shareManager->updateShare($share3);
845
-
846
-		// $request = $this->createRequest(['path' => $this->subfolder]);
847
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
848
-		$result1 = $ocs->getShares('false', 'false', 'false', $this->subfolder);
849
-		$ocs->cleanup();
850
-
851
-		// test should return one share within $this->folder
852
-		$data1 = $result1->getData();
853
-		$this->assertCount(1, $data1);
854
-		$s1 = reset($data1);
855
-
856
-		//$request = $this->createRequest(['path' => $this->folder.$this->subfolder]);
857
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
858
-		$result2 = $ocs->getShares('false', 'false', 'false', $this->folder . $this->subfolder);
859
-		$ocs->cleanup();
860
-
861
-		// test should return one share within $this->folder
862
-		$data2 = $result2->getData();
863
-		$this->assertCount(1, $data2);
864
-		$s2 = reset($data2);
865
-
866
-		$this->assertEquals($this->subfolder, $s1['path']);
867
-		$this->assertEquals($this->folder . $this->subfolder, $s2['path']);
868
-
869
-		$this->shareManager->deleteShare($share1);
870
-		$this->shareManager->deleteShare($share2);
871
-		$this->shareManager->deleteShare($share3);
872
-	}
873
-
874
-	/**
875
-	 * test re-re-share of folder if the path gets constructed correctly
876
-	 * @medium
877
-	 */
878
-	public function testGetShareFromFileReReShares(): void {
879
-		$node1 = $this->userFolder->get($this->folder . $this->subfolder);
880
-		$share1 = $this->shareManager->newShare();
881
-		$share1->setNode($node1)
882
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
883
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
884
-			->setShareType(IShare::TYPE_USER)
885
-			->setPermissions(31);
886
-		$share1 = $this->shareManager->createShare($share1);
887
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
888
-		$this->shareManager->updateShare($share1);
889
-
890
-		$user2Folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER2);
891
-		$node2 = $user2Folder->get($this->subfolder . $this->filename);
892
-		$share2 = $this->shareManager->newShare();
893
-		$share2->setNode($node2)
894
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
895
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
896
-			->setShareType(IShare::TYPE_USER)
897
-			->setPermissions(19);
898
-		$share2 = $this->shareManager->createShare($share2);
899
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
900
-		$this->shareManager->updateShare($share2);
901
-
902
-		$user3Folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER3);
903
-		$node3 = $user3Folder->get($this->filename);
904
-		$share3 = $this->shareManager->newShare();
905
-		$share3->setNode($node3)
906
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER3)
907
-			->setShareType(IShare::TYPE_LINK)
908
-			->setPermissions(1);
909
-		$share3 = $this->shareManager->createShare($share3);
910
-		$share3->setStatus(IShare::STATUS_ACCEPTED);
911
-		$this->shareManager->updateShare($share3);
912
-
913
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
914
-		$result = $ocs->getShares();
915
-		$ocs->cleanup();
916
-
917
-		// test should return one share within $this->folder
918
-		$data = $result->getData();
919
-
920
-		// we should get exactly one result
921
-		$this->assertCount(1, $data);
922
-
923
-		$this->assertEquals($this->filename, $data[0]['path']);
924
-
925
-		$this->shareManager->deleteShare($share1);
926
-		$this->shareManager->deleteShare($share2);
927
-		$this->shareManager->deleteShare($share3);
928
-	}
929
-
930
-	/**
931
-	 * @medium
932
-	 */
933
-	public function testGetShareFromUnknownId(): void {
934
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
935
-		try {
936
-			$ocs->getShare(0);
937
-			$this->fail();
938
-		} catch (OCSNotFoundException $e) {
939
-			$this->assertEquals('Wrong share ID, share does not exist', $e->getMessage());
940
-		}
941
-		$ocs->cleanup();
942
-	}
943
-
944
-	/**
945
-	 * @medium
946
-	 * @depends testCreateShareUserFile
947
-	 * @depends testCreateShareLink
948
-	 */
949
-	public function testUpdateShare(): void {
950
-		$password = md5(time());
951
-
952
-		$node1 = $this->userFolder->get($this->filename);
953
-		$share1 = $this->shareManager->newShare();
954
-		$share1->setNode($node1)
955
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
956
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
957
-			->setShareType(IShare::TYPE_USER)
958
-			->setPermissions(19)
959
-			->setAttributes($this->shareManager->newShare()->newAttributes());
960
-
961
-		$this->assertNotNull($share1->getAttributes());
962
-		$share1 = $this->shareManager->createShare($share1);
963
-		$this->assertEquals(19, $share1->getPermissions());
964
-
965
-		$share2 = $this->shareManager->newShare();
966
-		$share2->setNode($node1)
967
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
968
-			->setShareType(IShare::TYPE_LINK)
969
-			->setPermissions(1);
970
-		$share2 = $this->shareManager->createShare($share2);
971
-		$this->assertEquals(1, $share2->getPermissions());
972
-
973
-		// update permissions
974
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
975
-		$ocs->updateShare(
976
-			$share1->getId(), 1, null, null, null, null, null, null, null,
977
-			'[{"scope": "app1", "key": "attr1", "value": true}]'
978
-		);
979
-		$ocs->cleanup();
980
-
981
-		$share1 = $this->shareManager->getShareById('ocinternal:' . $share1->getId());
982
-		$this->assertEquals(1, $share1->getPermissions());
983
-		$this->assertEquals(true, $share1->getAttributes()->getAttribute('app1', 'attr1'));
984
-
985
-		// update password for link share
986
-		$this->assertNull($share2->getPassword());
987
-
988
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
989
-		$ocs->updateShare($share2->getId(), null, $password);
990
-		$ocs->cleanup();
991
-
992
-		$share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId());
993
-		$this->assertNotNull($share2->getPassword());
994
-
995
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
996
-		$ocs->updateShare($share2->getId(), null, '');
997
-		$ocs->cleanup();
998
-
999
-		$share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId());
1000
-		$this->assertNull($share2->getPassword());
1001
-
1002
-		$this->shareManager->deleteShare($share1);
1003
-		$this->shareManager->deleteShare($share2);
1004
-	}
1005
-
1006
-	/**
1007
-	 * @medium
1008
-	 */
1009
-	public function testUpdateShareUpload(): void {
1010
-		$node1 = $this->userFolder->get($this->folder);
1011
-		$share1 = $this->shareManager->newShare();
1012
-		$share1->setNode($node1)
1013
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1014
-			->setShareType(IShare::TYPE_LINK)
1015
-			->setPermissions(1);
1016
-		$share1 = $this->shareManager->createShare($share1);
1017
-
1018
-		// update public upload
1019
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1020
-		$ocs->updateShare($share1->getId(), null, null, null, 'true');
1021
-		$ocs->cleanup();
1022
-
1023
-		$share1 = $this->shareManager->getShareById($share1->getFullId());
1024
-		$this->assertEquals(
1025
-			Constants::PERMISSION_READ
1026
-			| Constants::PERMISSION_CREATE
1027
-			| Constants::PERMISSION_UPDATE
1028
-			| Constants::PERMISSION_DELETE
1029
-			| Constants::PERMISSION_SHARE,
1030
-			$share1->getPermissions()
1031
-		);
1032
-
1033
-		// cleanup
1034
-		$this->shareManager->deleteShare($share1);
1035
-	}
1036
-
1037
-	/**
1038
-	 * @medium
1039
-	 */
1040
-	public function testUpdateShareExpireDate(): void {
1041
-		$node1 = $this->userFolder->get($this->folder);
1042
-		$share1 = $this->shareManager->newShare();
1043
-		$share1->setNode($node1)
1044
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1045
-			->setShareType(IShare::TYPE_LINK)
1046
-			->setPermissions(1);
1047
-		$share1 = $this->shareManager->createShare($share1);
1048
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
1049
-		$this->shareManager->updateShare($share1);
1050
-
1051
-		$config = Server::get(IConfig::class);
1052
-
1053
-		// enforce expire date, by default 7 days after the file was shared
1054
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1055
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1056
-
1057
-		$dateWithinRange = new \DateTime();
1058
-		$dateWithinRange->add(new \DateInterval('P6D'));
1059
-
1060
-		$dateOutOfRange = new \DateTime();
1061
-		$dateOutOfRange->add(new \DateInterval('P8D'));
1062
-
1063
-		// update expire date to a valid value
1064
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1065
-		$ocs->updateShare($share1->getId(), null, null, null, null, $dateWithinRange->format('Y-m-d'));
1066
-		$ocs->cleanup();
1067
-
1068
-		$share1 = $this->shareManager->getShareById($share1->getFullId());
1069
-
1070
-		// date should be changed
1071
-		$dateWithinRange->setTime(0, 0, 0);
1072
-		$dateWithinRange->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1073
-		$this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1074
-
1075
-		// update expire date to a value out of range
1076
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1077
-		try {
1078
-			$ocs->updateShare($share1->getId());
1079
-			$this->fail();
1080
-		} catch (OCSBadRequestException $e) {
1081
-		}
1082
-		$ocs->cleanup();
1083
-
1084
-		$share1 = $this->shareManager->getShareById($share1->getFullId());
1085
-
1086
-		// date shouldn't be changed
1087
-		$this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1088
-
1089
-		// Try to remove expire date
1090
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1091
-		try {
1092
-			$ocs->updateShare($share1->getId());
1093
-			$this->fail();
1094
-		} catch (OCSBadRequestException $e) {
1095
-		}
1096
-		$ocs->cleanup();
1097
-
1098
-		$share1 = $this->shareManager->getShareById($share1->getFullId());
1099
-
1100
-
1101
-		// date shouldn't be changed
1102
-		$this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1103
-		// cleanup
1104
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1105
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1106
-		$this->shareManager->deleteShare($share1);
1107
-	}
1108
-
1109
-	/**
1110
-	 * @medium
1111
-	 * @depends testCreateShareUserFile
1112
-	 */
1113
-	public function testDeleteShare(): void {
1114
-		$node1 = $this->userFolder->get($this->filename);
1115
-		$share1 = $this->shareManager->newShare();
1116
-		$share1->setNode($node1)
1117
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1118
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
1119
-			->setShareType(IShare::TYPE_USER)
1120
-			->setPermissions(19);
1121
-		$share1 = $this->shareManager->createShare($share1);
1122
-
1123
-		$share2 = $this->shareManager->newShare();
1124
-		$share2->setNode($node1)
1125
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1126
-			->setShareType(IShare::TYPE_LINK)
1127
-			->setPermissions(1);
1128
-		$share2 = $this->shareManager->createShare($share2);
1129
-
1130
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1131
-		$ocs->deleteShare($share1->getId());
1132
-		$ocs->cleanup();
1133
-
1134
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1135
-		$ocs->deleteShare($share2->getId());
1136
-		$ocs->cleanup();
1137
-
1138
-		$this->assertEmpty($this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER2, IShare::TYPE_USER));
1139
-		$this->assertEmpty($this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER2, IShare::TYPE_LINK));
1140
-	}
1141
-
1142
-	/**
1143
-	 * test unshare of a reshared file
1144
-	 */
1145
-	public function testDeleteReshare(): void {
1146
-		$node1 = $this->userFolder->get($this->folder);
1147
-		$share1 = $this->shareManager->newShare();
1148
-		$share1->setNode($node1)
1149
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1150
-			->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
1151
-			->setShareType(IShare::TYPE_USER)
1152
-			->setPermissions(31);
1153
-		$share1 = $this->shareManager->createShare($share1);
1154
-		$share1->setStatus(IShare::STATUS_ACCEPTED);
1155
-		$this->shareManager->updateShare($share1);
1156
-
1157
-		$user2folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER2);
1158
-		$node2 = $user2folder->get($this->folder . '/' . $this->filename);
1159
-		$share2 = $this->shareManager->newShare();
1160
-		$share2->setNode($node2)
1161
-			->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
1162
-			->setShareType(IShare::TYPE_LINK)
1163
-			->setPermissions(1);
1164
-		$share2 = $this->shareManager->createShare($share2);
1165
-		$share2->setStatus(IShare::STATUS_ACCEPTED);
1166
-		$this->shareManager->updateShare($share2);
1167
-
1168
-		// test if we can unshare the link again
1169
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1170
-		$ocs->deleteShare($share2->getId());
1171
-		$ocs->cleanup();
1172
-
1173
-		$this->shareManager->deleteShare($share1);
1174
-		$this->addToAssertionCount(1);
1175
-	}
1176
-
1177
-	/**
1178
-	 * share a folder which contains a share mount point, should be forbidden
1179
-	 */
1180
-	public function testShareFolderWithAMountPoint(): void {
1181
-		// user 1 shares a folder with user2
1182
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1183
-
1184
-		$share = $this->share(
1185
-			IShare::TYPE_USER,
1186
-			$this->folder,
1187
-			self::TEST_FILES_SHARING_API_USER1,
1188
-			self::TEST_FILES_SHARING_API_USER2,
1189
-			Constants::PERMISSION_ALL
1190
-		);
1191
-		$share->setStatus(IShare::STATUS_ACCEPTED);
1192
-		$this->shareManager->updateShare($share);
1193
-
1194
-		// user2 shares a file from the folder as link
1195
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
1196
-
1197
-		$view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
1198
-		$view->mkdir('localDir');
1199
-
1200
-		// move mount point to the folder "localDir"
1201
-		$result = $view->rename($this->folder, 'localDir/' . $this->folder);
1202
-		$this->assertTrue($result !== false);
1203
-
1204
-		// try to share "localDir"
1205
-		$fileInfo2 = $view->getFileInfo('localDir');
1206
-
1207
-		$this->assertTrue($fileInfo2 instanceof FileInfo);
1208
-
1209
-		$pass = true;
1210
-		try {
1211
-			$this->share(
1212
-				IShare::TYPE_USER,
1213
-				'localDir',
1214
-				self::TEST_FILES_SHARING_API_USER2,
1215
-				self::TEST_FILES_SHARING_API_USER3,
1216
-				Constants::PERMISSION_ALL
1217
-			);
1218
-		} catch (\Exception $e) {
1219
-			$pass = false;
1220
-		}
1221
-
1222
-		$this->assertFalse($pass);
1223
-
1224
-		//cleanup
1225
-
1226
-		$result = $view->rename('localDir/' . $this->folder, $this->folder);
1227
-		$this->assertTrue($result !== false);
1228
-		$view->unlink('localDir');
1229
-
1230
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1231
-
1232
-		$this->shareManager->deleteShare($share);
1233
-	}
1234
-
1235
-	/**
1236
-	 * Post init mount points hook for mounting simulated ext storage
1237
-	 */
1238
-	public static function initTestMountPointsHook($data) {
1239
-		if ($data['user'] === self::TEST_FILES_SHARING_API_USER1) {
1240
-			Filesystem::mount(self::$tempStorage, [], '/' . self::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME);
1241
-		}
1242
-	}
1243
-
1244
-	/**
1245
-	 * Tests mounting a folder that is an external storage mount point.
1246
-	 */
1247
-	public function testShareStorageMountPoint(): void {
1248
-		$tempStorage = new Temporary([]);
1249
-		$tempStorage->file_put_contents('test.txt', 'abcdef');
1250
-		$tempStorage->getScanner()->scan('');
1251
-
1252
-		$this->registerMount(self::TEST_FILES_SHARING_API_USER1, $tempStorage, self::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME);
1253
-
1254
-		// logging in will auto-mount the temp storage for user1 as well
1255
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1256
-
1257
-		// user 1 shares the mount point folder with user2
1258
-		$share = $this->share(
1259
-			IShare::TYPE_USER,
1260
-			$this->folder,
1261
-			self::TEST_FILES_SHARING_API_USER1,
1262
-			self::TEST_FILES_SHARING_API_USER2,
1263
-			Constants::PERMISSION_ALL
1264
-		);
1265
-		$share->setStatus(IShare::STATUS_ACCEPTED);
1266
-		$this->shareManager->updateShare($share);
1267
-
1268
-		// user2: check that mount point name appears correctly
1269
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
1270
-
1271
-		$view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
1272
-
1273
-		$this->assertTrue($view->file_exists($this->folder));
1274
-		$this->assertTrue($view->file_exists($this->folder . '/test.txt'));
1275
-
1276
-		self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1277
-
1278
-		$this->shareManager->deleteShare($share);
1279
-
1280
-		\OC_Hook::clear('OC_Filesystem', 'post_initMountPoints');
1281
-		\OC_Hook::clear('\OCA\Files_Sharing\Tests\ApiTest', 'initTestMountPointsHook');
1282
-	}
1283
-
1284
-	public static function datesProvider() {
1285
-		$date = new \DateTime();
1286
-		$date->setTime(0, 0);
1287
-		$date->add(new \DateInterval('P5D'));
1288
-		$date->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1289
-
1290
-		return [
1291
-			[$date->format('Y-m-d H:i:s'), true],
1292
-			['abc', false],
1293
-			[$date->format('Y-m-d H:i:s') . 'xyz', false],
1294
-		];
1295
-	}
1296
-
1297
-	/**
1298
-	 * Make sure only ISO 8601 dates are accepted
1299
-	 *
1300
-	 * @group RoutingWeirdness
1301
-	 */
1302
-	#[\PHPUnit\Framework\Attributes\DataProvider('datesProvider')]
1303
-	public function testPublicLinkExpireDate($date, $valid): void {
1304
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1305
-
1306
-		try {
1307
-			$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date);
1308
-			$this->assertTrue($valid);
1309
-		} catch (OCSNotFoundException $e) {
1310
-			$this->assertFalse($valid);
1311
-			$this->assertEquals('Invalid date. Format must be YYYY-MM-DD', $e->getMessage());
1312
-			$ocs->cleanup();
1313
-			return;
1314
-		}
1315
-		$ocs->cleanup();
1316
-
1317
-		$data = $result->getData();
1318
-		$this->assertTrue(is_string($data['token']));
1319
-		$this->assertEquals(substr($date, 0, 10), substr($data['expiration'], 0, 10));
1320
-
1321
-		// check for correct link
1322
-		$url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
1323
-		$this->assertEquals($url, $data['url']);
1324
-
1325
-		$share = $this->shareManager->getShareById('ocinternal:' . $data['id']);
1326
-
1327
-		$this->assertEquals($date, $share->getExpirationDate()->format('Y-m-d H:i:s'));
1328
-
1329
-		$this->shareManager->deleteShare($share);
1330
-	}
1331
-
1332
-	/**
1333
-	 * @group RoutingWeirdness
1334
-	 */
1335
-	public function testCreatePublicLinkExpireDateValid(): void {
1336
-		$config = Server::get(IConfig::class);
1337
-
1338
-		// enforce expire date, by default 7 days after the file was shared
1339
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1340
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1341
-
1342
-		$date = new \DateTime();
1343
-		$date->add(new \DateInterval('P5D'));
1344
-
1345
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1346
-		$result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1347
-		$ocs->cleanup();
1348
-
1349
-		$data = $result->getData();
1350
-		$this->assertTrue(is_string($data['token']));
1351
-		$this->assertEquals($date->format('Y-m-d 00:00:00'), $data['expiration']);
1352
-
1353
-		// check for correct link
1354
-		$url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
1355
-		$this->assertEquals($url, $data['url']);
1356
-
1357
-		$share = $this->shareManager->getShareById('ocinternal:' . $data['id']);
1358
-		$date->setTime(0, 0, 0);
1359
-		$this->assertEquals($date, $share->getExpirationDate());
1360
-
1361
-		$this->shareManager->deleteShare($share);
1362
-
1363
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1364
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1365
-	}
1366
-
1367
-	public function testCreatePublicLinkExpireDateInvalidFuture(): void {
1368
-		$config = Server::get(IConfig::class);
1369
-
1370
-		// enforce expire date, by default 7 days after the file was shared
1371
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1372
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1373
-
1374
-		$date = new \DateTime();
1375
-		$date->add(new \DateInterval('P8D'));
1376
-
1377
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1378
-
1379
-		try {
1380
-			$ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1381
-			$this->fail();
1382
-		} catch (OCSException $e) {
1383
-			$this->assertEquals(404, $e->getCode());
1384
-			$this->assertEquals('Cannot set expiration date more than 7 days in the future', $e->getMessage());
1385
-		}
1386
-		$ocs->cleanup();
1387
-
1388
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1389
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1390
-	}
1391
-
1392
-	public function XtestCreatePublicLinkExpireDateInvalidPast() {
1393
-		$config = Server::get(IConfig::class);
1394
-
1395
-		$date = new \DateTime();
1396
-		$date->sub(new \DateInterval('P8D'));
1397
-
1398
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1399
-
1400
-		try {
1401
-			$ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1402
-			$this->fail();
1403
-		} catch (OCSException $e) {
1404
-			$this->assertEquals(404, $e->getCode());
1405
-			$this->assertEquals('Expiration date is in the past', $e->getMessage());
1406
-		}
1407
-		$ocs->cleanup();
1408
-
1409
-		$config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1410
-		$config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1411
-	}
1412
-
1413
-	/**
1414
-	 * test for no invisible shares
1415
-	 * See: https://github.com/owncloud/core/issues/22295
1416
-	 */
1417
-	public function testInvisibleSharesUser(): void {
1418
-		// simulate a post request
1419
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1420
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
1421
-		$ocs->cleanup();
1422
-		$data = $result->getData();
793
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
794
+        $result = $ocs->getShares();
795
+        $ocs->cleanup();
796
+
797
+        // test should return one share within $this->folder
798
+        $data = $result->getData();
799
+
800
+        // we should get exactly one result
801
+        $this->assertCount(1, $data);
802
+        $this->assertEquals($this->subfolder . $this->subsubfolder, $data[0]['path']);
803
+
804
+        $this->shareManager->deleteShare($share1);
805
+        $this->shareManager->deleteShare($share2);
806
+        $this->shareManager->deleteShare($share3);
807
+    }
808
+
809
+    /**
810
+     * test multiple shared folder if the path gets constructed correctly
811
+     * @medium
812
+     */
813
+    public function testGetShareMultipleSharedFolder(): void {
814
+        $this->setUp();
815
+        $node1 = $this->userFolder->get($this->folder . $this->subfolder);
816
+        $share1 = $this->shareManager->newShare();
817
+        $share1->setNode($node1)
818
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
819
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
820
+            ->setShareType(IShare::TYPE_USER)
821
+            ->setPermissions(31);
822
+        $share1 = $this->shareManager->createShare($share1);
823
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
824
+        $this->shareManager->updateShare($share1);
825
+
826
+        $node2 = $this->userFolder->get($this->folder);
827
+        $share2 = $this->shareManager->newShare();
828
+        $share2->setNode($node2)
829
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
830
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
831
+            ->setShareType(IShare::TYPE_USER)
832
+            ->setPermissions(31);
833
+        $share2 = $this->shareManager->createShare($share2);
834
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
835
+        $this->shareManager->updateShare($share2);
836
+
837
+        $share3 = $this->shareManager->newShare();
838
+        $share3->setNode($node1)
839
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
840
+            ->setShareType(IShare::TYPE_LINK)
841
+            ->setPermissions(1);
842
+        $share3 = $this->shareManager->createShare($share3);
843
+        $share3->setStatus(IShare::STATUS_ACCEPTED);
844
+        $this->shareManager->updateShare($share3);
845
+
846
+        // $request = $this->createRequest(['path' => $this->subfolder]);
847
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
848
+        $result1 = $ocs->getShares('false', 'false', 'false', $this->subfolder);
849
+        $ocs->cleanup();
850
+
851
+        // test should return one share within $this->folder
852
+        $data1 = $result1->getData();
853
+        $this->assertCount(1, $data1);
854
+        $s1 = reset($data1);
855
+
856
+        //$request = $this->createRequest(['path' => $this->folder.$this->subfolder]);
857
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
858
+        $result2 = $ocs->getShares('false', 'false', 'false', $this->folder . $this->subfolder);
859
+        $ocs->cleanup();
860
+
861
+        // test should return one share within $this->folder
862
+        $data2 = $result2->getData();
863
+        $this->assertCount(1, $data2);
864
+        $s2 = reset($data2);
865
+
866
+        $this->assertEquals($this->subfolder, $s1['path']);
867
+        $this->assertEquals($this->folder . $this->subfolder, $s2['path']);
868
+
869
+        $this->shareManager->deleteShare($share1);
870
+        $this->shareManager->deleteShare($share2);
871
+        $this->shareManager->deleteShare($share3);
872
+    }
873
+
874
+    /**
875
+     * test re-re-share of folder if the path gets constructed correctly
876
+     * @medium
877
+     */
878
+    public function testGetShareFromFileReReShares(): void {
879
+        $node1 = $this->userFolder->get($this->folder . $this->subfolder);
880
+        $share1 = $this->shareManager->newShare();
881
+        $share1->setNode($node1)
882
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
883
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
884
+            ->setShareType(IShare::TYPE_USER)
885
+            ->setPermissions(31);
886
+        $share1 = $this->shareManager->createShare($share1);
887
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
888
+        $this->shareManager->updateShare($share1);
889
+
890
+        $user2Folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER2);
891
+        $node2 = $user2Folder->get($this->subfolder . $this->filename);
892
+        $share2 = $this->shareManager->newShare();
893
+        $share2->setNode($node2)
894
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
895
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER3)
896
+            ->setShareType(IShare::TYPE_USER)
897
+            ->setPermissions(19);
898
+        $share2 = $this->shareManager->createShare($share2);
899
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
900
+        $this->shareManager->updateShare($share2);
901
+
902
+        $user3Folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER3);
903
+        $node3 = $user3Folder->get($this->filename);
904
+        $share3 = $this->shareManager->newShare();
905
+        $share3->setNode($node3)
906
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER3)
907
+            ->setShareType(IShare::TYPE_LINK)
908
+            ->setPermissions(1);
909
+        $share3 = $this->shareManager->createShare($share3);
910
+        $share3->setStatus(IShare::STATUS_ACCEPTED);
911
+        $this->shareManager->updateShare($share3);
912
+
913
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
914
+        $result = $ocs->getShares();
915
+        $ocs->cleanup();
916
+
917
+        // test should return one share within $this->folder
918
+        $data = $result->getData();
919
+
920
+        // we should get exactly one result
921
+        $this->assertCount(1, $data);
922
+
923
+        $this->assertEquals($this->filename, $data[0]['path']);
924
+
925
+        $this->shareManager->deleteShare($share1);
926
+        $this->shareManager->deleteShare($share2);
927
+        $this->shareManager->deleteShare($share3);
928
+    }
929
+
930
+    /**
931
+     * @medium
932
+     */
933
+    public function testGetShareFromUnknownId(): void {
934
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER3);
935
+        try {
936
+            $ocs->getShare(0);
937
+            $this->fail();
938
+        } catch (OCSNotFoundException $e) {
939
+            $this->assertEquals('Wrong share ID, share does not exist', $e->getMessage());
940
+        }
941
+        $ocs->cleanup();
942
+    }
943
+
944
+    /**
945
+     * @medium
946
+     * @depends testCreateShareUserFile
947
+     * @depends testCreateShareLink
948
+     */
949
+    public function testUpdateShare(): void {
950
+        $password = md5(time());
951
+
952
+        $node1 = $this->userFolder->get($this->filename);
953
+        $share1 = $this->shareManager->newShare();
954
+        $share1->setNode($node1)
955
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
956
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
957
+            ->setShareType(IShare::TYPE_USER)
958
+            ->setPermissions(19)
959
+            ->setAttributes($this->shareManager->newShare()->newAttributes());
960
+
961
+        $this->assertNotNull($share1->getAttributes());
962
+        $share1 = $this->shareManager->createShare($share1);
963
+        $this->assertEquals(19, $share1->getPermissions());
964
+
965
+        $share2 = $this->shareManager->newShare();
966
+        $share2->setNode($node1)
967
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
968
+            ->setShareType(IShare::TYPE_LINK)
969
+            ->setPermissions(1);
970
+        $share2 = $this->shareManager->createShare($share2);
971
+        $this->assertEquals(1, $share2->getPermissions());
972
+
973
+        // update permissions
974
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
975
+        $ocs->updateShare(
976
+            $share1->getId(), 1, null, null, null, null, null, null, null,
977
+            '[{"scope": "app1", "key": "attr1", "value": true}]'
978
+        );
979
+        $ocs->cleanup();
980
+
981
+        $share1 = $this->shareManager->getShareById('ocinternal:' . $share1->getId());
982
+        $this->assertEquals(1, $share1->getPermissions());
983
+        $this->assertEquals(true, $share1->getAttributes()->getAttribute('app1', 'attr1'));
984
+
985
+        // update password for link share
986
+        $this->assertNull($share2->getPassword());
987
+
988
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
989
+        $ocs->updateShare($share2->getId(), null, $password);
990
+        $ocs->cleanup();
991
+
992
+        $share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId());
993
+        $this->assertNotNull($share2->getPassword());
994
+
995
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
996
+        $ocs->updateShare($share2->getId(), null, '');
997
+        $ocs->cleanup();
998
+
999
+        $share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId());
1000
+        $this->assertNull($share2->getPassword());
1001
+
1002
+        $this->shareManager->deleteShare($share1);
1003
+        $this->shareManager->deleteShare($share2);
1004
+    }
1005
+
1006
+    /**
1007
+     * @medium
1008
+     */
1009
+    public function testUpdateShareUpload(): void {
1010
+        $node1 = $this->userFolder->get($this->folder);
1011
+        $share1 = $this->shareManager->newShare();
1012
+        $share1->setNode($node1)
1013
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1014
+            ->setShareType(IShare::TYPE_LINK)
1015
+            ->setPermissions(1);
1016
+        $share1 = $this->shareManager->createShare($share1);
1017
+
1018
+        // update public upload
1019
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1020
+        $ocs->updateShare($share1->getId(), null, null, null, 'true');
1021
+        $ocs->cleanup();
1022
+
1023
+        $share1 = $this->shareManager->getShareById($share1->getFullId());
1024
+        $this->assertEquals(
1025
+            Constants::PERMISSION_READ
1026
+            | Constants::PERMISSION_CREATE
1027
+            | Constants::PERMISSION_UPDATE
1028
+            | Constants::PERMISSION_DELETE
1029
+            | Constants::PERMISSION_SHARE,
1030
+            $share1->getPermissions()
1031
+        );
1032
+
1033
+        // cleanup
1034
+        $this->shareManager->deleteShare($share1);
1035
+    }
1036
+
1037
+    /**
1038
+     * @medium
1039
+     */
1040
+    public function testUpdateShareExpireDate(): void {
1041
+        $node1 = $this->userFolder->get($this->folder);
1042
+        $share1 = $this->shareManager->newShare();
1043
+        $share1->setNode($node1)
1044
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1045
+            ->setShareType(IShare::TYPE_LINK)
1046
+            ->setPermissions(1);
1047
+        $share1 = $this->shareManager->createShare($share1);
1048
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
1049
+        $this->shareManager->updateShare($share1);
1050
+
1051
+        $config = Server::get(IConfig::class);
1052
+
1053
+        // enforce expire date, by default 7 days after the file was shared
1054
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1055
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1056
+
1057
+        $dateWithinRange = new \DateTime();
1058
+        $dateWithinRange->add(new \DateInterval('P6D'));
1059
+
1060
+        $dateOutOfRange = new \DateTime();
1061
+        $dateOutOfRange->add(new \DateInterval('P8D'));
1062
+
1063
+        // update expire date to a valid value
1064
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1065
+        $ocs->updateShare($share1->getId(), null, null, null, null, $dateWithinRange->format('Y-m-d'));
1066
+        $ocs->cleanup();
1067
+
1068
+        $share1 = $this->shareManager->getShareById($share1->getFullId());
1069
+
1070
+        // date should be changed
1071
+        $dateWithinRange->setTime(0, 0, 0);
1072
+        $dateWithinRange->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1073
+        $this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1074
+
1075
+        // update expire date to a value out of range
1076
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1077
+        try {
1078
+            $ocs->updateShare($share1->getId());
1079
+            $this->fail();
1080
+        } catch (OCSBadRequestException $e) {
1081
+        }
1082
+        $ocs->cleanup();
1083
+
1084
+        $share1 = $this->shareManager->getShareById($share1->getFullId());
1085
+
1086
+        // date shouldn't be changed
1087
+        $this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1088
+
1089
+        // Try to remove expire date
1090
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1091
+        try {
1092
+            $ocs->updateShare($share1->getId());
1093
+            $this->fail();
1094
+        } catch (OCSBadRequestException $e) {
1095
+        }
1096
+        $ocs->cleanup();
1097
+
1098
+        $share1 = $this->shareManager->getShareById($share1->getFullId());
1099
+
1100
+
1101
+        // date shouldn't be changed
1102
+        $this->assertEquals($dateWithinRange, $share1->getExpirationDate());
1103
+        // cleanup
1104
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1105
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1106
+        $this->shareManager->deleteShare($share1);
1107
+    }
1108
+
1109
+    /**
1110
+     * @medium
1111
+     * @depends testCreateShareUserFile
1112
+     */
1113
+    public function testDeleteShare(): void {
1114
+        $node1 = $this->userFolder->get($this->filename);
1115
+        $share1 = $this->shareManager->newShare();
1116
+        $share1->setNode($node1)
1117
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1118
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
1119
+            ->setShareType(IShare::TYPE_USER)
1120
+            ->setPermissions(19);
1121
+        $share1 = $this->shareManager->createShare($share1);
1122
+
1123
+        $share2 = $this->shareManager->newShare();
1124
+        $share2->setNode($node1)
1125
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1126
+            ->setShareType(IShare::TYPE_LINK)
1127
+            ->setPermissions(1);
1128
+        $share2 = $this->shareManager->createShare($share2);
1129
+
1130
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1131
+        $ocs->deleteShare($share1->getId());
1132
+        $ocs->cleanup();
1133
+
1134
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1135
+        $ocs->deleteShare($share2->getId());
1136
+        $ocs->cleanup();
1137
+
1138
+        $this->assertEmpty($this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER2, IShare::TYPE_USER));
1139
+        $this->assertEmpty($this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER2, IShare::TYPE_LINK));
1140
+    }
1141
+
1142
+    /**
1143
+     * test unshare of a reshared file
1144
+     */
1145
+    public function testDeleteReshare(): void {
1146
+        $node1 = $this->userFolder->get($this->folder);
1147
+        $share1 = $this->shareManager->newShare();
1148
+        $share1->setNode($node1)
1149
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER1)
1150
+            ->setSharedWith(self::TEST_FILES_SHARING_API_USER2)
1151
+            ->setShareType(IShare::TYPE_USER)
1152
+            ->setPermissions(31);
1153
+        $share1 = $this->shareManager->createShare($share1);
1154
+        $share1->setStatus(IShare::STATUS_ACCEPTED);
1155
+        $this->shareManager->updateShare($share1);
1156
+
1157
+        $user2folder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER2);
1158
+        $node2 = $user2folder->get($this->folder . '/' . $this->filename);
1159
+        $share2 = $this->shareManager->newShare();
1160
+        $share2->setNode($node2)
1161
+            ->setSharedBy(self::TEST_FILES_SHARING_API_USER2)
1162
+            ->setShareType(IShare::TYPE_LINK)
1163
+            ->setPermissions(1);
1164
+        $share2 = $this->shareManager->createShare($share2);
1165
+        $share2->setStatus(IShare::STATUS_ACCEPTED);
1166
+        $this->shareManager->updateShare($share2);
1167
+
1168
+        // test if we can unshare the link again
1169
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1170
+        $ocs->deleteShare($share2->getId());
1171
+        $ocs->cleanup();
1172
+
1173
+        $this->shareManager->deleteShare($share1);
1174
+        $this->addToAssertionCount(1);
1175
+    }
1176
+
1177
+    /**
1178
+     * share a folder which contains a share mount point, should be forbidden
1179
+     */
1180
+    public function testShareFolderWithAMountPoint(): void {
1181
+        // user 1 shares a folder with user2
1182
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1183
+
1184
+        $share = $this->share(
1185
+            IShare::TYPE_USER,
1186
+            $this->folder,
1187
+            self::TEST_FILES_SHARING_API_USER1,
1188
+            self::TEST_FILES_SHARING_API_USER2,
1189
+            Constants::PERMISSION_ALL
1190
+        );
1191
+        $share->setStatus(IShare::STATUS_ACCEPTED);
1192
+        $this->shareManager->updateShare($share);
1193
+
1194
+        // user2 shares a file from the folder as link
1195
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
1196
+
1197
+        $view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
1198
+        $view->mkdir('localDir');
1199
+
1200
+        // move mount point to the folder "localDir"
1201
+        $result = $view->rename($this->folder, 'localDir/' . $this->folder);
1202
+        $this->assertTrue($result !== false);
1203
+
1204
+        // try to share "localDir"
1205
+        $fileInfo2 = $view->getFileInfo('localDir');
1206
+
1207
+        $this->assertTrue($fileInfo2 instanceof FileInfo);
1208
+
1209
+        $pass = true;
1210
+        try {
1211
+            $this->share(
1212
+                IShare::TYPE_USER,
1213
+                'localDir',
1214
+                self::TEST_FILES_SHARING_API_USER2,
1215
+                self::TEST_FILES_SHARING_API_USER3,
1216
+                Constants::PERMISSION_ALL
1217
+            );
1218
+        } catch (\Exception $e) {
1219
+            $pass = false;
1220
+        }
1221
+
1222
+        $this->assertFalse($pass);
1223
+
1224
+        //cleanup
1225
+
1226
+        $result = $view->rename('localDir/' . $this->folder, $this->folder);
1227
+        $this->assertTrue($result !== false);
1228
+        $view->unlink('localDir');
1229
+
1230
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1231
+
1232
+        $this->shareManager->deleteShare($share);
1233
+    }
1234
+
1235
+    /**
1236
+     * Post init mount points hook for mounting simulated ext storage
1237
+     */
1238
+    public static function initTestMountPointsHook($data) {
1239
+        if ($data['user'] === self::TEST_FILES_SHARING_API_USER1) {
1240
+            Filesystem::mount(self::$tempStorage, [], '/' . self::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME);
1241
+        }
1242
+    }
1243
+
1244
+    /**
1245
+     * Tests mounting a folder that is an external storage mount point.
1246
+     */
1247
+    public function testShareStorageMountPoint(): void {
1248
+        $tempStorage = new Temporary([]);
1249
+        $tempStorage->file_put_contents('test.txt', 'abcdef');
1250
+        $tempStorage->getScanner()->scan('');
1251
+
1252
+        $this->registerMount(self::TEST_FILES_SHARING_API_USER1, $tempStorage, self::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME);
1253
+
1254
+        // logging in will auto-mount the temp storage for user1 as well
1255
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1256
+
1257
+        // user 1 shares the mount point folder with user2
1258
+        $share = $this->share(
1259
+            IShare::TYPE_USER,
1260
+            $this->folder,
1261
+            self::TEST_FILES_SHARING_API_USER1,
1262
+            self::TEST_FILES_SHARING_API_USER2,
1263
+            Constants::PERMISSION_ALL
1264
+        );
1265
+        $share->setStatus(IShare::STATUS_ACCEPTED);
1266
+        $this->shareManager->updateShare($share);
1267
+
1268
+        // user2: check that mount point name appears correctly
1269
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
1270
+
1271
+        $view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
1272
+
1273
+        $this->assertTrue($view->file_exists($this->folder));
1274
+        $this->assertTrue($view->file_exists($this->folder . '/test.txt'));
1275
+
1276
+        self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
1277
+
1278
+        $this->shareManager->deleteShare($share);
1279
+
1280
+        \OC_Hook::clear('OC_Filesystem', 'post_initMountPoints');
1281
+        \OC_Hook::clear('\OCA\Files_Sharing\Tests\ApiTest', 'initTestMountPointsHook');
1282
+    }
1283
+
1284
+    public static function datesProvider() {
1285
+        $date = new \DateTime();
1286
+        $date->setTime(0, 0);
1287
+        $date->add(new \DateInterval('P5D'));
1288
+        $date->setTimezone(new \DateTimeZone(date_default_timezone_get()));
1289
+
1290
+        return [
1291
+            [$date->format('Y-m-d H:i:s'), true],
1292
+            ['abc', false],
1293
+            [$date->format('Y-m-d H:i:s') . 'xyz', false],
1294
+        ];
1295
+    }
1296
+
1297
+    /**
1298
+     * Make sure only ISO 8601 dates are accepted
1299
+     *
1300
+     * @group RoutingWeirdness
1301
+     */
1302
+    #[\PHPUnit\Framework\Attributes\DataProvider('datesProvider')]
1303
+    public function testPublicLinkExpireDate($date, $valid): void {
1304
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1305
+
1306
+        try {
1307
+            $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date);
1308
+            $this->assertTrue($valid);
1309
+        } catch (OCSNotFoundException $e) {
1310
+            $this->assertFalse($valid);
1311
+            $this->assertEquals('Invalid date. Format must be YYYY-MM-DD', $e->getMessage());
1312
+            $ocs->cleanup();
1313
+            return;
1314
+        }
1315
+        $ocs->cleanup();
1316
+
1317
+        $data = $result->getData();
1318
+        $this->assertTrue(is_string($data['token']));
1319
+        $this->assertEquals(substr($date, 0, 10), substr($data['expiration'], 0, 10));
1320
+
1321
+        // check for correct link
1322
+        $url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
1323
+        $this->assertEquals($url, $data['url']);
1324
+
1325
+        $share = $this->shareManager->getShareById('ocinternal:' . $data['id']);
1326
+
1327
+        $this->assertEquals($date, $share->getExpirationDate()->format('Y-m-d H:i:s'));
1328
+
1329
+        $this->shareManager->deleteShare($share);
1330
+    }
1331
+
1332
+    /**
1333
+     * @group RoutingWeirdness
1334
+     */
1335
+    public function testCreatePublicLinkExpireDateValid(): void {
1336
+        $config = Server::get(IConfig::class);
1337
+
1338
+        // enforce expire date, by default 7 days after the file was shared
1339
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1340
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1341
+
1342
+        $date = new \DateTime();
1343
+        $date->add(new \DateInterval('P5D'));
1344
+
1345
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1346
+        $result = $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1347
+        $ocs->cleanup();
1348
+
1349
+        $data = $result->getData();
1350
+        $this->assertTrue(is_string($data['token']));
1351
+        $this->assertEquals($date->format('Y-m-d 00:00:00'), $data['expiration']);
1352
+
1353
+        // check for correct link
1354
+        $url = Server::get(IURLGenerator::class)->getAbsoluteURL('/index.php/s/' . $data['token']);
1355
+        $this->assertEquals($url, $data['url']);
1356
+
1357
+        $share = $this->shareManager->getShareById('ocinternal:' . $data['id']);
1358
+        $date->setTime(0, 0, 0);
1359
+        $this->assertEquals($date, $share->getExpirationDate());
1360
+
1361
+        $this->shareManager->deleteShare($share);
1362
+
1363
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1364
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1365
+    }
1366
+
1367
+    public function testCreatePublicLinkExpireDateInvalidFuture(): void {
1368
+        $config = Server::get(IConfig::class);
1369
+
1370
+        // enforce expire date, by default 7 days after the file was shared
1371
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'yes');
1372
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'yes');
1373
+
1374
+        $date = new \DateTime();
1375
+        $date->add(new \DateInterval('P8D'));
1376
+
1377
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1378
+
1379
+        try {
1380
+            $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1381
+            $this->fail();
1382
+        } catch (OCSException $e) {
1383
+            $this->assertEquals(404, $e->getCode());
1384
+            $this->assertEquals('Cannot set expiration date more than 7 days in the future', $e->getMessage());
1385
+        }
1386
+        $ocs->cleanup();
1387
+
1388
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1389
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1390
+    }
1391
+
1392
+    public function XtestCreatePublicLinkExpireDateInvalidPast() {
1393
+        $config = Server::get(IConfig::class);
1394
+
1395
+        $date = new \DateTime();
1396
+        $date->sub(new \DateInterval('P8D'));
1397
+
1398
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1399
+
1400
+        try {
1401
+            $ocs->createShare($this->filename, Constants::PERMISSION_ALL, IShare::TYPE_LINK, null, 'false', '', null, $date->format('Y-m-d'));
1402
+            $this->fail();
1403
+        } catch (OCSException $e) {
1404
+            $this->assertEquals(404, $e->getCode());
1405
+            $this->assertEquals('Expiration date is in the past', $e->getMessage());
1406
+        }
1407
+        $ocs->cleanup();
1408
+
1409
+        $config->setAppValue('core', 'shareapi_default_expire_date', 'no');
1410
+        $config->setAppValue('core', 'shareapi_enforce_expire_date', 'no');
1411
+    }
1412
+
1413
+    /**
1414
+     * test for no invisible shares
1415
+     * See: https://github.com/owncloud/core/issues/22295
1416
+     */
1417
+    public function testInvisibleSharesUser(): void {
1418
+        // simulate a post request
1419
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1420
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_USER, self::TEST_FILES_SHARING_API_USER2);
1421
+        $ocs->cleanup();
1422
+        $data = $result->getData();
1423 1423
 
1424
-		$topId = $data['id'];
1425
-
1426
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1427
-		$ocs->acceptShare($topId);
1428
-		$ocs->cleanup();
1429
-
1430
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1431
-		$ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
1432
-		$ocs->cleanup();
1433
-
1434
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1435
-		$ocs->deleteShare($topId);
1436
-		$ocs->cleanup();
1437
-
1438
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1439
-		$result = $ocs->getShares();
1440
-		$ocs->cleanup();
1441
-
1442
-		$this->assertEmpty($result->getData());
1443
-	}
1444
-
1445
-	/**
1446
-	 * test for no invisible shares
1447
-	 * See: https://github.com/owncloud/core/issues/22295
1448
-	 */
1449
-	public function testInvisibleSharesGroup(): void {
1450
-		// simulate a post request
1451
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1452
-		$result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
1453
-		$ocs->cleanup();
1454
-		$data = $result->getData();
1455
-
1456
-		$topId = $data['id'];
1457
-
1458
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1459
-		$ocs->acceptShare($topId);
1460
-		$ocs->cleanup();
1461
-
1462
-		\OC_Util::tearDownFS();
1463
-
1464
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1465
-		$ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
1466
-		$ocs->cleanup();
1467
-
1468
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1469
-		$ocs->deleteShare($topId);
1470
-		$ocs->cleanup();
1471
-
1472
-		$ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1473
-		$result = $ocs->getShares();
1474
-		$ocs->cleanup();
1475
-
1476
-		$this->assertEmpty($result->getData());
1477
-	}
1424
+        $topId = $data['id'];
1425
+
1426
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1427
+        $ocs->acceptShare($topId);
1428
+        $ocs->cleanup();
1429
+
1430
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1431
+        $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
1432
+        $ocs->cleanup();
1433
+
1434
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1435
+        $ocs->deleteShare($topId);
1436
+        $ocs->cleanup();
1437
+
1438
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1439
+        $result = $ocs->getShares();
1440
+        $ocs->cleanup();
1441
+
1442
+        $this->assertEmpty($result->getData());
1443
+    }
1444
+
1445
+    /**
1446
+     * test for no invisible shares
1447
+     * See: https://github.com/owncloud/core/issues/22295
1448
+     */
1449
+    public function testInvisibleSharesGroup(): void {
1450
+        // simulate a post request
1451
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1452
+        $result = $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_GROUP, self::TEST_FILES_SHARING_API_GROUP1);
1453
+        $ocs->cleanup();
1454
+        $data = $result->getData();
1455
+
1456
+        $topId = $data['id'];
1457
+
1458
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1459
+        $ocs->acceptShare($topId);
1460
+        $ocs->cleanup();
1461
+
1462
+        \OC_Util::tearDownFS();
1463
+
1464
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2);
1465
+        $ocs->createShare($this->folder, Constants::PERMISSION_ALL, IShare::TYPE_LINK);
1466
+        $ocs->cleanup();
1467
+
1468
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1469
+        $ocs->deleteShare($topId);
1470
+        $ocs->cleanup();
1471
+
1472
+        $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1);
1473
+        $result = $ocs->getShares();
1474
+        $ocs->cleanup();
1475
+
1476
+        $this->assertEmpty($result->getData());
1477
+    }
1478 1478
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/ApplicationTest.php 1 patch
Indentation   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -23,184 +23,184 @@
 block discarded – undo
23 23
 use Test\TestCase;
24 24
 
25 25
 class ApplicationTest extends TestCase {
26
-	private Application $application;
27
-
28
-	/** @var IUserSession */
29
-	private $userSession;
30
-
31
-	/** @var IRootFolder */
32
-	private $rootFolder;
33
-
34
-
35
-	protected function setUp(): void {
36
-		parent::setUp();
37
-
38
-		$this->application = new Application([]);
39
-
40
-		$this->userSession = $this->createMock(IUserSession::class);
41
-		$this->rootFolder = $this->createMock(IRootFolder::class);
42
-	}
43
-
44
-	public function providesDataForCanGet(): array {
45
-		// normal file (sender) - can download directly
46
-		$senderFileStorage = $this->createMock(IStorage::class);
47
-		$senderFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
48
-		$senderFile = $this->createMock(File::class);
49
-		$senderFile->method('getStorage')->willReturn($senderFileStorage);
50
-		$senderUserFolder = $this->createMock(Folder::class);
51
-		$senderUserFolder->method('get')->willReturn($senderFile);
52
-
53
-		$result[] = [ '/bar.txt', $senderUserFolder, true ];
54
-
55
-		// shared file (receiver) with attribute secure-view-enabled set false -
56
-		// can download directly
57
-		$receiverFileShareAttributes = $this->createMock(IAttributes::class);
58
-		$receiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
59
-		$receiverFileShare = $this->createMock(IShare::class);
60
-		$receiverFileShare->method('getAttributes')->willReturn($receiverFileShareAttributes);
61
-		$receiverFileStorage = $this->createMock(SharedStorage::class);
62
-		$receiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
63
-		$receiverFileStorage->method('getShare')->willReturn($receiverFileShare);
64
-		$receiverFile = $this->createMock(File::class);
65
-		$receiverFile->method('getStorage')->willReturn($receiverFileStorage);
66
-		$receiverUserFolder = $this->createMock(Folder::class);
67
-		$receiverUserFolder->method('get')->willReturn($receiverFile);
68
-
69
-		$result[] = [ '/share-bar.txt', $receiverUserFolder, true ];
70
-
71
-		// shared file (receiver) with attribute secure-view-enabled set true -
72
-		// cannot download directly
73
-		$secureReceiverFileShareAttributes = $this->createMock(IAttributes::class);
74
-		$secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false);
75
-		$secureReceiverFileShare = $this->createMock(IShare::class);
76
-		$secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes);
77
-		$secureReceiverFileStorage = $this->createMock(SharedStorage::class);
78
-		$secureReceiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
79
-		$secureReceiverFileStorage->method('getShare')->willReturn($secureReceiverFileShare);
80
-		$secureReceiverFile = $this->createMock(File::class);
81
-		$secureReceiverFile->method('getStorage')->willReturn($secureReceiverFileStorage);
82
-		$secureReceiverUserFolder = $this->createMock(Folder::class);
83
-		$secureReceiverUserFolder->method('get')->willReturn($secureReceiverFile);
84
-
85
-		$result[] = [ '/secure-share-bar.txt', $secureReceiverUserFolder, false ];
86
-
87
-		return $result;
88
-	}
89
-
90
-	#[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanGet')]
91
-	public function testCheckDirectCanBeDownloaded(string $path, Folder $userFolder, bool $run): void {
92
-		$user = $this->createMock(IUser::class);
93
-		$user->method('getUID')->willReturn('test');
94
-		$this->userSession->method('getUser')->willReturn($user);
95
-		$this->userSession->method('isLoggedIn')->willReturn(true);
96
-		$this->rootFolder->method('getUserFolder')->willReturn($userFolder);
97
-
98
-		// Simulate direct download of file
99
-		$event = new BeforeDirectFileDownloadEvent($path);
100
-		$listener = new BeforeDirectFileDownloadListener(
101
-			$this->userSession,
102
-			$this->rootFolder
103
-		);
104
-		$listener->handle($event);
105
-
106
-		$this->assertEquals($run, $event->isSuccessful());
107
-	}
108
-
109
-	public function providesDataForCanZip(): array {
110
-		// Mock: Normal file/folder storage
111
-		$nonSharedStorage = $this->createMock(IStorage::class);
112
-		$nonSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
113
-
114
-		// Mock: Secure-view file/folder shared storage
115
-		$secureReceiverFileShareAttributes = $this->createMock(IAttributes::class);
116
-		$secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false);
117
-		$secureReceiverFileShare = $this->createMock(IShare::class);
118
-		$secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes);
119
-		$secureSharedStorage = $this->createMock(SharedStorage::class);
120
-		$secureSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
121
-		$secureSharedStorage->method('getShare')->willReturn($secureReceiverFileShare);
122
-
123
-		// 1. can download zipped 2 non-shared files inside non-shared folder
124
-		// 2. can download zipped non-shared folder
125
-		$sender1File = $this->createMock(File::class);
126
-		$sender1File->method('getStorage')->willReturn($nonSharedStorage);
127
-		$sender1Folder = $this->createMock(Folder::class);
128
-		$sender1Folder->method('getStorage')->willReturn($nonSharedStorage);
129
-		$sender1Folder->method('getDirectoryListing')->willReturn([$sender1File, $sender1File]);
130
-		$sender1RootFolder = $this->createMock(Folder::class);
131
-		$sender1RootFolder->method('getStorage')->willReturn($nonSharedStorage);
132
-		$sender1RootFolder->method('getDirectoryListing')->willReturn([$sender1Folder]);
133
-		$sender1UserFolder = $this->createMock(Folder::class);
134
-		$sender1UserFolder->method('get')->willReturn($sender1RootFolder);
135
-
136
-		$return[] = [ '/folder', ['bar1.txt', 'bar2.txt'], $sender1UserFolder, true ];
137
-		$return[] = [ '/', ['folder'], $sender1UserFolder, true ];
138
-
139
-		// 3. cannot download zipped 1 non-shared file and 1 secure-shared inside non-shared folder
140
-		$receiver1File = $this->createMock(File::class);
141
-		$receiver1File->method('getStorage')->willReturn($nonSharedStorage);
142
-		$receiver1SecureFile = $this->createMock(File::class);
143
-		$receiver1SecureFile->method('getStorage')->willReturn($secureSharedStorage);
144
-		$receiver1Folder = $this->createMock(Folder::class);
145
-		$receiver1Folder->method('getStorage')->willReturn($nonSharedStorage);
146
-		$receiver1Folder->method('getDirectoryListing')->willReturn([$receiver1File, $receiver1SecureFile]);
147
-		$receiver1RootFolder = $this->createMock(Folder::class);
148
-		$receiver1RootFolder->method('getStorage')->willReturn($nonSharedStorage);
149
-		$receiver1RootFolder->method('getDirectoryListing')->willReturn([$receiver1Folder]);
150
-		$receiver1UserFolder = $this->createMock(Folder::class);
151
-		$receiver1UserFolder->method('get')->willReturn($receiver1RootFolder);
152
-
153
-		$return[] = [ '/folder', ['secured-bar1.txt', 'bar2.txt'], $receiver1UserFolder, false ];
154
-
155
-		// 4. cannot download zipped secure-shared folder
156
-		$receiver2Folder = $this->createMock(Folder::class);
157
-		$receiver2Folder->method('getStorage')->willReturn($secureSharedStorage);
158
-		$receiver2RootFolder = $this->createMock(Folder::class);
159
-		$receiver2RootFolder->method('getStorage')->willReturn($nonSharedStorage);
160
-		$receiver2RootFolder->method('getDirectoryListing')->willReturn([$receiver2Folder]);
161
-		$receiver2UserFolder = $this->createMock(Folder::class);
162
-		$receiver2UserFolder->method('get')->willReturn($receiver2RootFolder);
163
-
164
-		$return[] = [ '/', ['secured-folder'], $receiver2UserFolder, false ];
165
-
166
-		return $return;
167
-	}
168
-
169
-	#[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanZip')]
170
-	public function testCheckZipCanBeDownloaded(string $dir, array $files, Folder $userFolder, bool $run): void {
171
-		$user = $this->createMock(IUser::class);
172
-		$user->method('getUID')->willReturn('test');
173
-		$this->userSession->method('getUser')->willReturn($user);
174
-		$this->userSession->method('isLoggedIn')->willReturn(true);
175
-
176
-		$this->rootFolder->method('getUserFolder')->with('test')->willReturn($userFolder);
177
-
178
-		// Simulate zip download of folder folder
179
-		$event = new BeforeZipCreatedEvent($dir, $files);
180
-		$listener = new BeforeZipCreatedListener(
181
-			$this->userSession,
182
-			$this->rootFolder
183
-		);
184
-		$listener->handle($event);
185
-
186
-
187
-		$this->assertEquals($run, $event->isSuccessful());
188
-		$this->assertEquals($run, $event->getErrorMessage() === null);
189
-	}
190
-
191
-	public function testCheckFileUserNotFound(): void {
192
-		$this->userSession->method('isLoggedIn')->willReturn(false);
193
-
194
-		// Simulate zip download of folder folder
195
-		$event = new BeforeZipCreatedEvent('/test', ['test.txt']);
196
-		$listener = new BeforeZipCreatedListener(
197
-			$this->userSession,
198
-			$this->rootFolder
199
-		);
200
-		$listener->handle($event);
201
-
202
-		// It should run as this would restrict e.g. share links otherwise
203
-		$this->assertTrue($event->isSuccessful());
204
-		$this->assertEquals(null, $event->getErrorMessage());
205
-	}
26
+    private Application $application;
27
+
28
+    /** @var IUserSession */
29
+    private $userSession;
30
+
31
+    /** @var IRootFolder */
32
+    private $rootFolder;
33
+
34
+
35
+    protected function setUp(): void {
36
+        parent::setUp();
37
+
38
+        $this->application = new Application([]);
39
+
40
+        $this->userSession = $this->createMock(IUserSession::class);
41
+        $this->rootFolder = $this->createMock(IRootFolder::class);
42
+    }
43
+
44
+    public function providesDataForCanGet(): array {
45
+        // normal file (sender) - can download directly
46
+        $senderFileStorage = $this->createMock(IStorage::class);
47
+        $senderFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
48
+        $senderFile = $this->createMock(File::class);
49
+        $senderFile->method('getStorage')->willReturn($senderFileStorage);
50
+        $senderUserFolder = $this->createMock(Folder::class);
51
+        $senderUserFolder->method('get')->willReturn($senderFile);
52
+
53
+        $result[] = [ '/bar.txt', $senderUserFolder, true ];
54
+
55
+        // shared file (receiver) with attribute secure-view-enabled set false -
56
+        // can download directly
57
+        $receiverFileShareAttributes = $this->createMock(IAttributes::class);
58
+        $receiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(true);
59
+        $receiverFileShare = $this->createMock(IShare::class);
60
+        $receiverFileShare->method('getAttributes')->willReturn($receiverFileShareAttributes);
61
+        $receiverFileStorage = $this->createMock(SharedStorage::class);
62
+        $receiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
63
+        $receiverFileStorage->method('getShare')->willReturn($receiverFileShare);
64
+        $receiverFile = $this->createMock(File::class);
65
+        $receiverFile->method('getStorage')->willReturn($receiverFileStorage);
66
+        $receiverUserFolder = $this->createMock(Folder::class);
67
+        $receiverUserFolder->method('get')->willReturn($receiverFile);
68
+
69
+        $result[] = [ '/share-bar.txt', $receiverUserFolder, true ];
70
+
71
+        // shared file (receiver) with attribute secure-view-enabled set true -
72
+        // cannot download directly
73
+        $secureReceiverFileShareAttributes = $this->createMock(IAttributes::class);
74
+        $secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false);
75
+        $secureReceiverFileShare = $this->createMock(IShare::class);
76
+        $secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes);
77
+        $secureReceiverFileStorage = $this->createMock(SharedStorage::class);
78
+        $secureReceiverFileStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
79
+        $secureReceiverFileStorage->method('getShare')->willReturn($secureReceiverFileShare);
80
+        $secureReceiverFile = $this->createMock(File::class);
81
+        $secureReceiverFile->method('getStorage')->willReturn($secureReceiverFileStorage);
82
+        $secureReceiverUserFolder = $this->createMock(Folder::class);
83
+        $secureReceiverUserFolder->method('get')->willReturn($secureReceiverFile);
84
+
85
+        $result[] = [ '/secure-share-bar.txt', $secureReceiverUserFolder, false ];
86
+
87
+        return $result;
88
+    }
89
+
90
+    #[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanGet')]
91
+    public function testCheckDirectCanBeDownloaded(string $path, Folder $userFolder, bool $run): void {
92
+        $user = $this->createMock(IUser::class);
93
+        $user->method('getUID')->willReturn('test');
94
+        $this->userSession->method('getUser')->willReturn($user);
95
+        $this->userSession->method('isLoggedIn')->willReturn(true);
96
+        $this->rootFolder->method('getUserFolder')->willReturn($userFolder);
97
+
98
+        // Simulate direct download of file
99
+        $event = new BeforeDirectFileDownloadEvent($path);
100
+        $listener = new BeforeDirectFileDownloadListener(
101
+            $this->userSession,
102
+            $this->rootFolder
103
+        );
104
+        $listener->handle($event);
105
+
106
+        $this->assertEquals($run, $event->isSuccessful());
107
+    }
108
+
109
+    public function providesDataForCanZip(): array {
110
+        // Mock: Normal file/folder storage
111
+        $nonSharedStorage = $this->createMock(IStorage::class);
112
+        $nonSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
113
+
114
+        // Mock: Secure-view file/folder shared storage
115
+        $secureReceiverFileShareAttributes = $this->createMock(IAttributes::class);
116
+        $secureReceiverFileShareAttributes->method('getAttribute')->with('permissions', 'download')->willReturn(false);
117
+        $secureReceiverFileShare = $this->createMock(IShare::class);
118
+        $secureReceiverFileShare->method('getAttributes')->willReturn($secureReceiverFileShareAttributes);
119
+        $secureSharedStorage = $this->createMock(SharedStorage::class);
120
+        $secureSharedStorage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
121
+        $secureSharedStorage->method('getShare')->willReturn($secureReceiverFileShare);
122
+
123
+        // 1. can download zipped 2 non-shared files inside non-shared folder
124
+        // 2. can download zipped non-shared folder
125
+        $sender1File = $this->createMock(File::class);
126
+        $sender1File->method('getStorage')->willReturn($nonSharedStorage);
127
+        $sender1Folder = $this->createMock(Folder::class);
128
+        $sender1Folder->method('getStorage')->willReturn($nonSharedStorage);
129
+        $sender1Folder->method('getDirectoryListing')->willReturn([$sender1File, $sender1File]);
130
+        $sender1RootFolder = $this->createMock(Folder::class);
131
+        $sender1RootFolder->method('getStorage')->willReturn($nonSharedStorage);
132
+        $sender1RootFolder->method('getDirectoryListing')->willReturn([$sender1Folder]);
133
+        $sender1UserFolder = $this->createMock(Folder::class);
134
+        $sender1UserFolder->method('get')->willReturn($sender1RootFolder);
135
+
136
+        $return[] = [ '/folder', ['bar1.txt', 'bar2.txt'], $sender1UserFolder, true ];
137
+        $return[] = [ '/', ['folder'], $sender1UserFolder, true ];
138
+
139
+        // 3. cannot download zipped 1 non-shared file and 1 secure-shared inside non-shared folder
140
+        $receiver1File = $this->createMock(File::class);
141
+        $receiver1File->method('getStorage')->willReturn($nonSharedStorage);
142
+        $receiver1SecureFile = $this->createMock(File::class);
143
+        $receiver1SecureFile->method('getStorage')->willReturn($secureSharedStorage);
144
+        $receiver1Folder = $this->createMock(Folder::class);
145
+        $receiver1Folder->method('getStorage')->willReturn($nonSharedStorage);
146
+        $receiver1Folder->method('getDirectoryListing')->willReturn([$receiver1File, $receiver1SecureFile]);
147
+        $receiver1RootFolder = $this->createMock(Folder::class);
148
+        $receiver1RootFolder->method('getStorage')->willReturn($nonSharedStorage);
149
+        $receiver1RootFolder->method('getDirectoryListing')->willReturn([$receiver1Folder]);
150
+        $receiver1UserFolder = $this->createMock(Folder::class);
151
+        $receiver1UserFolder->method('get')->willReturn($receiver1RootFolder);
152
+
153
+        $return[] = [ '/folder', ['secured-bar1.txt', 'bar2.txt'], $receiver1UserFolder, false ];
154
+
155
+        // 4. cannot download zipped secure-shared folder
156
+        $receiver2Folder = $this->createMock(Folder::class);
157
+        $receiver2Folder->method('getStorage')->willReturn($secureSharedStorage);
158
+        $receiver2RootFolder = $this->createMock(Folder::class);
159
+        $receiver2RootFolder->method('getStorage')->willReturn($nonSharedStorage);
160
+        $receiver2RootFolder->method('getDirectoryListing')->willReturn([$receiver2Folder]);
161
+        $receiver2UserFolder = $this->createMock(Folder::class);
162
+        $receiver2UserFolder->method('get')->willReturn($receiver2RootFolder);
163
+
164
+        $return[] = [ '/', ['secured-folder'], $receiver2UserFolder, false ];
165
+
166
+        return $return;
167
+    }
168
+
169
+    #[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanZip')]
170
+    public function testCheckZipCanBeDownloaded(string $dir, array $files, Folder $userFolder, bool $run): void {
171
+        $user = $this->createMock(IUser::class);
172
+        $user->method('getUID')->willReturn('test');
173
+        $this->userSession->method('getUser')->willReturn($user);
174
+        $this->userSession->method('isLoggedIn')->willReturn(true);
175
+
176
+        $this->rootFolder->method('getUserFolder')->with('test')->willReturn($userFolder);
177
+
178
+        // Simulate zip download of folder folder
179
+        $event = new BeforeZipCreatedEvent($dir, $files);
180
+        $listener = new BeforeZipCreatedListener(
181
+            $this->userSession,
182
+            $this->rootFolder
183
+        );
184
+        $listener->handle($event);
185
+
186
+
187
+        $this->assertEquals($run, $event->isSuccessful());
188
+        $this->assertEquals($run, $event->getErrorMessage() === null);
189
+    }
190
+
191
+    public function testCheckFileUserNotFound(): void {
192
+        $this->userSession->method('isLoggedIn')->willReturn(false);
193
+
194
+        // Simulate zip download of folder folder
195
+        $event = new BeforeZipCreatedEvent('/test', ['test.txt']);
196
+        $listener = new BeforeZipCreatedListener(
197
+            $this->userSession,
198
+            $this->rootFolder
199
+        );
200
+        $listener->handle($event);
201
+
202
+        // It should run as this would restrict e.g. share links otherwise
203
+        $this->assertTrue($event->isSuccessful());
204
+        $this->assertEquals(null, $event->getErrorMessage());
205
+    }
206 206
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/MountProviderTest.php 2 patches
Indentation   +354 added lines, -354 removed lines patch added patch discarded remove patch
@@ -30,380 +30,380 @@
 block discarded – undo
30 30
  */
31 31
 class MountProviderTest extends \Test\TestCase {
32 32
 
33
-	protected MountProvider $provider;
33
+    protected MountProvider $provider;
34 34
 
35
-	protected IUser&MockObject $user;
36
-	protected IConfig&MockObject $config;
37
-	protected IManager&MockObject $shareManager;
38
-	protected IStorageFactory&MockObject $loader;
39
-	protected LoggerInterface&MockObject $logger;
35
+    protected IUser&MockObject $user;
36
+    protected IConfig&MockObject $config;
37
+    protected IManager&MockObject $shareManager;
38
+    protected IStorageFactory&MockObject $loader;
39
+    protected LoggerInterface&MockObject $logger;
40 40
 
41
-	protected function setUp(): void {
42
-		parent::setUp();
41
+    protected function setUp(): void {
42
+        parent::setUp();
43 43
 
44
-		$this->config = $this->getMockBuilder(IConfig::class)->getMock();
45
-		$this->user = $this->getMockBuilder(IUser::class)->getMock();
46
-		$this->loader = $this->getMockBuilder('OCP\Files\Storage\IStorageFactory')->getMock();
47
-		$this->shareManager = $this->getMockBuilder(IManager::class)->getMock();
48
-		$this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
49
-		$eventDispatcher = $this->createMock(IEventDispatcher::class);
50
-		$cacheFactory = $this->createMock(ICacheFactory::class);
51
-		$cacheFactory->method('createLocal')
52
-			->willReturn(new NullCache());
53
-		$mountManager = $this->createMock(IMountManager::class);
44
+        $this->config = $this->getMockBuilder(IConfig::class)->getMock();
45
+        $this->user = $this->getMockBuilder(IUser::class)->getMock();
46
+        $this->loader = $this->getMockBuilder('OCP\Files\Storage\IStorageFactory')->getMock();
47
+        $this->shareManager = $this->getMockBuilder(IManager::class)->getMock();
48
+        $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
49
+        $eventDispatcher = $this->createMock(IEventDispatcher::class);
50
+        $cacheFactory = $this->createMock(ICacheFactory::class);
51
+        $cacheFactory->method('createLocal')
52
+            ->willReturn(new NullCache());
53
+        $mountManager = $this->createMock(IMountManager::class);
54 54
 
55
-		$this->provider = new MountProvider($this->config, $this->shareManager, $this->logger, $eventDispatcher, $cacheFactory, $mountManager);
56
-	}
55
+        $this->provider = new MountProvider($this->config, $this->shareManager, $this->logger, $eventDispatcher, $cacheFactory, $mountManager);
56
+    }
57 57
 
58
-	private function makeMockShareAttributes($attrs) {
59
-		if ($attrs === null) {
60
-			return null;
61
-		}
58
+    private function makeMockShareAttributes($attrs) {
59
+        if ($attrs === null) {
60
+            return null;
61
+        }
62 62
 
63
-		$shareAttributes = $this->createMock(IShareAttributes::class);
64
-		$shareAttributes->method('toArray')->willReturn($attrs);
65
-		$shareAttributes->method('getAttribute')->willReturnCallback(
66
-			function ($scope, $key) use ($attrs) {
67
-				$result = null;
68
-				foreach ($attrs as $attr) {
69
-					if ($attr['key'] === $key && $attr['scope'] === $scope) {
70
-						$result = $attr['value'];
71
-					}
72
-				}
73
-				return $result;
74
-			}
75
-		);
76
-		return $shareAttributes;
77
-	}
63
+        $shareAttributes = $this->createMock(IShareAttributes::class);
64
+        $shareAttributes->method('toArray')->willReturn($attrs);
65
+        $shareAttributes->method('getAttribute')->willReturnCallback(
66
+            function ($scope, $key) use ($attrs) {
67
+                $result = null;
68
+                foreach ($attrs as $attr) {
69
+                    if ($attr['key'] === $key && $attr['scope'] === $scope) {
70
+                        $result = $attr['value'];
71
+                    }
72
+                }
73
+                return $result;
74
+            }
75
+        );
76
+        return $shareAttributes;
77
+    }
78 78
 
79
-	private function makeMockShare($id, $nodeId, $owner = 'user2', $target = null, $permissions = 31, $attributes = null) {
80
-		$share = $this->createMock(IShare::class);
81
-		$share->expects($this->any())
82
-			->method('getPermissions')
83
-			->willReturn($permissions);
84
-		$share->expects($this->any())
85
-			->method('getAttributes')
86
-			->willReturn($this->makeMockShareAttributes($attributes));
87
-		$share->expects($this->any())
88
-			->method('getShareOwner')
89
-			->willReturn($owner);
90
-		$share->expects($this->any())
91
-			->method('getTarget')
92
-			->willReturn($target);
93
-		$share->expects($this->any())
94
-			->method('getId')
95
-			->willReturn($id);
96
-		$share->expects($this->any())
97
-			->method('getNodeId')
98
-			->willReturn($nodeId);
99
-		$share->expects($this->any())
100
-			->method('getShareTime')
101
-			->willReturn(
102
-				// compute share time based on id, simulating share order
103
-				new \DateTime('@' . (1469193980 + 1000 * $id))
104
-			);
105
-		return $share;
106
-	}
79
+    private function makeMockShare($id, $nodeId, $owner = 'user2', $target = null, $permissions = 31, $attributes = null) {
80
+        $share = $this->createMock(IShare::class);
81
+        $share->expects($this->any())
82
+            ->method('getPermissions')
83
+            ->willReturn($permissions);
84
+        $share->expects($this->any())
85
+            ->method('getAttributes')
86
+            ->willReturn($this->makeMockShareAttributes($attributes));
87
+        $share->expects($this->any())
88
+            ->method('getShareOwner')
89
+            ->willReturn($owner);
90
+        $share->expects($this->any())
91
+            ->method('getTarget')
92
+            ->willReturn($target);
93
+        $share->expects($this->any())
94
+            ->method('getId')
95
+            ->willReturn($id);
96
+        $share->expects($this->any())
97
+            ->method('getNodeId')
98
+            ->willReturn($nodeId);
99
+        $share->expects($this->any())
100
+            ->method('getShareTime')
101
+            ->willReturn(
102
+                // compute share time based on id, simulating share order
103
+                new \DateTime('@' . (1469193980 + 1000 * $id))
104
+            );
105
+        return $share;
106
+    }
107 107
 
108
-	/**
109
-	 * Tests excluding shares from the current view. This includes:
110
-	 * - shares that were opted out of (permissions === 0)
111
-	 * - shares with a group in which the owner is already in
112
-	 */
113
-	public function testExcludeShares(): void {
114
-		$rootFolder = $this->createMock(IRootFolder::class);
115
-		$userManager = $this->createMock(IUserManager::class);
116
-		$attr1 = [];
117
-		$attr2 = [['scope' => 'permission', 'key' => 'download', 'value' => true]];
118
-		$userShares = [
119
-			$this->makeMockShare(1, 100, 'user2', '/share2', 0, $attr1),
120
-			$this->makeMockShare(2, 100, 'user2', '/share2', 31, $attr2),
121
-		];
122
-		$groupShares = [
123
-			$this->makeMockShare(3, 100, 'user2', '/share2', 0, $attr1),
124
-			$this->makeMockShare(4, 101, 'user2', '/share4', 31, $attr2),
125
-			$this->makeMockShare(5, 100, 'user1', '/share4', 31, $attr2),
126
-		];
127
-		$roomShares = [
128
-			$this->makeMockShare(6, 102, 'user2', '/share6', 0),
129
-			$this->makeMockShare(7, 102, 'user1', '/share6', 31),
130
-			$this->makeMockShare(8, 102, 'user2', '/share6', 31),
131
-			$this->makeMockShare(9, 102, 'user2', '/share6', 31),
132
-		];
133
-		$deckShares = [
134
-			$this->makeMockShare(10, 103, 'user2', '/share7', 0),
135
-			$this->makeMockShare(11, 103, 'user1', '/share7', 31),
136
-			$this->makeMockShare(12, 103, 'user2', '/share7', 31),
137
-			$this->makeMockShare(13, 103, 'user2', '/share7', 31),
138
-		];
139
-		// tests regarding circles and sciencemesh are made in the apps themselves.
140
-		$circleShares = [];
141
-		$scienceMeshShares = [];
142
-		$this->user->expects($this->any())
143
-			->method('getUID')
144
-			->willReturn('user1');
145
-		$this->shareManager->expects($this->exactly(6))
146
-			->method('getSharedWith')
147
-			->willReturnMap([
148
-				['user1', IShare::TYPE_USER, null, -1, 0, $userShares],
149
-				['user1', IShare::TYPE_GROUP, null, -1, 0, $groupShares],
150
-				['user1', IShare::TYPE_CIRCLE, null, -1, 0, $circleShares],
151
-				['user1', IShare::TYPE_ROOM, null, -1, 0, $roomShares],
152
-				['user1', IShare::TYPE_DECK, null, -1, 0, $deckShares],
153
-				['user1', IShare::TYPE_SCIENCEMESH, null, -1, 0, $scienceMeshShares],
154
-			]);
108
+    /**
109
+     * Tests excluding shares from the current view. This includes:
110
+     * - shares that were opted out of (permissions === 0)
111
+     * - shares with a group in which the owner is already in
112
+     */
113
+    public function testExcludeShares(): void {
114
+        $rootFolder = $this->createMock(IRootFolder::class);
115
+        $userManager = $this->createMock(IUserManager::class);
116
+        $attr1 = [];
117
+        $attr2 = [['scope' => 'permission', 'key' => 'download', 'value' => true]];
118
+        $userShares = [
119
+            $this->makeMockShare(1, 100, 'user2', '/share2', 0, $attr1),
120
+            $this->makeMockShare(2, 100, 'user2', '/share2', 31, $attr2),
121
+        ];
122
+        $groupShares = [
123
+            $this->makeMockShare(3, 100, 'user2', '/share2', 0, $attr1),
124
+            $this->makeMockShare(4, 101, 'user2', '/share4', 31, $attr2),
125
+            $this->makeMockShare(5, 100, 'user1', '/share4', 31, $attr2),
126
+        ];
127
+        $roomShares = [
128
+            $this->makeMockShare(6, 102, 'user2', '/share6', 0),
129
+            $this->makeMockShare(7, 102, 'user1', '/share6', 31),
130
+            $this->makeMockShare(8, 102, 'user2', '/share6', 31),
131
+            $this->makeMockShare(9, 102, 'user2', '/share6', 31),
132
+        ];
133
+        $deckShares = [
134
+            $this->makeMockShare(10, 103, 'user2', '/share7', 0),
135
+            $this->makeMockShare(11, 103, 'user1', '/share7', 31),
136
+            $this->makeMockShare(12, 103, 'user2', '/share7', 31),
137
+            $this->makeMockShare(13, 103, 'user2', '/share7', 31),
138
+        ];
139
+        // tests regarding circles and sciencemesh are made in the apps themselves.
140
+        $circleShares = [];
141
+        $scienceMeshShares = [];
142
+        $this->user->expects($this->any())
143
+            ->method('getUID')
144
+            ->willReturn('user1');
145
+        $this->shareManager->expects($this->exactly(6))
146
+            ->method('getSharedWith')
147
+            ->willReturnMap([
148
+                ['user1', IShare::TYPE_USER, null, -1, 0, $userShares],
149
+                ['user1', IShare::TYPE_GROUP, null, -1, 0, $groupShares],
150
+                ['user1', IShare::TYPE_CIRCLE, null, -1, 0, $circleShares],
151
+                ['user1', IShare::TYPE_ROOM, null, -1, 0, $roomShares],
152
+                ['user1', IShare::TYPE_DECK, null, -1, 0, $deckShares],
153
+                ['user1', IShare::TYPE_SCIENCEMESH, null, -1, 0, $scienceMeshShares],
154
+            ]);
155 155
 
156
-		$this->shareManager->expects($this->any())
157
-			->method('newShare')
158
-			->willReturnCallback(function () use ($rootFolder, $userManager) {
159
-				return new Share($rootFolder, $userManager);
160
-			});
156
+        $this->shareManager->expects($this->any())
157
+            ->method('newShare')
158
+            ->willReturnCallback(function () use ($rootFolder, $userManager) {
159
+                return new Share($rootFolder, $userManager);
160
+            });
161 161
 
162
-		$mounts = $this->provider->getMountsForUser($this->user, $this->loader);
163
-		$this->assertCount(4, $mounts);
164
-		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[0]);
165
-		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[1]);
166
-		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[2]);
167
-		$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[3]);
168
-		/** @var SharedMount[] $mounts */
169
-		$mountedShare1 = $mounts[0]->getShare();
170
-		$this->assertEquals('2', $mountedShare1->getId());
171
-		$this->assertEquals('user2', $mountedShare1->getShareOwner());
172
-		$this->assertEquals(100, $mountedShare1->getNodeId());
173
-		$this->assertEquals('/share2', $mountedShare1->getTarget());
174
-		$this->assertEquals(31, $mountedShare1->getPermissions());
175
-		$this->assertEquals(true, $mountedShare1->getAttributes()->getAttribute('permission', 'download'));
176
-		$mountedShare2 = $mounts[1]->getShare();
177
-		$this->assertEquals('4', $mountedShare2->getId());
178
-		$this->assertEquals('user2', $mountedShare2->getShareOwner());
179
-		$this->assertEquals(101, $mountedShare2->getNodeId());
180
-		$this->assertEquals('/share4', $mountedShare2->getTarget());
181
-		$this->assertEquals(31, $mountedShare2->getPermissions());
182
-		$this->assertEquals(true, $mountedShare2->getAttributes()->getAttribute('permission', 'download'));
183
-		$mountedShare3 = $mounts[2]->getShare();
184
-		$this->assertEquals('8', $mountedShare3->getId());
185
-		$this->assertEquals('user2', $mountedShare3->getShareOwner());
186
-		$this->assertEquals(102, $mountedShare3->getNodeId());
187
-		$this->assertEquals('/share6', $mountedShare3->getTarget());
188
-		$this->assertEquals(31, $mountedShare3->getPermissions());
189
-		$mountedShare4 = $mounts[3]->getShare();
190
-		$this->assertEquals('12', $mountedShare4->getId());
191
-		$this->assertEquals('user2', $mountedShare4->getShareOwner());
192
-		$this->assertEquals(103, $mountedShare4->getNodeId());
193
-		$this->assertEquals('/share7', $mountedShare4->getTarget());
194
-		$this->assertEquals(31, $mountedShare4->getPermissions());
195
-	}
162
+        $mounts = $this->provider->getMountsForUser($this->user, $this->loader);
163
+        $this->assertCount(4, $mounts);
164
+        $this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[0]);
165
+        $this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[1]);
166
+        $this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[2]);
167
+        $this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mounts[3]);
168
+        /** @var SharedMount[] $mounts */
169
+        $mountedShare1 = $mounts[0]->getShare();
170
+        $this->assertEquals('2', $mountedShare1->getId());
171
+        $this->assertEquals('user2', $mountedShare1->getShareOwner());
172
+        $this->assertEquals(100, $mountedShare1->getNodeId());
173
+        $this->assertEquals('/share2', $mountedShare1->getTarget());
174
+        $this->assertEquals(31, $mountedShare1->getPermissions());
175
+        $this->assertEquals(true, $mountedShare1->getAttributes()->getAttribute('permission', 'download'));
176
+        $mountedShare2 = $mounts[1]->getShare();
177
+        $this->assertEquals('4', $mountedShare2->getId());
178
+        $this->assertEquals('user2', $mountedShare2->getShareOwner());
179
+        $this->assertEquals(101, $mountedShare2->getNodeId());
180
+        $this->assertEquals('/share4', $mountedShare2->getTarget());
181
+        $this->assertEquals(31, $mountedShare2->getPermissions());
182
+        $this->assertEquals(true, $mountedShare2->getAttributes()->getAttribute('permission', 'download'));
183
+        $mountedShare3 = $mounts[2]->getShare();
184
+        $this->assertEquals('8', $mountedShare3->getId());
185
+        $this->assertEquals('user2', $mountedShare3->getShareOwner());
186
+        $this->assertEquals(102, $mountedShare3->getNodeId());
187
+        $this->assertEquals('/share6', $mountedShare3->getTarget());
188
+        $this->assertEquals(31, $mountedShare3->getPermissions());
189
+        $mountedShare4 = $mounts[3]->getShare();
190
+        $this->assertEquals('12', $mountedShare4->getId());
191
+        $this->assertEquals('user2', $mountedShare4->getShareOwner());
192
+        $this->assertEquals(103, $mountedShare4->getNodeId());
193
+        $this->assertEquals('/share7', $mountedShare4->getTarget());
194
+        $this->assertEquals(31, $mountedShare4->getPermissions());
195
+    }
196 196
 
197
-	public static function mergeSharesDataProvider(): array {
198
-		// note: the user in the specs here is the shareOwner not recipient
199
-		// the recipient is always "user1"
200
-		return [
201
-			// #0: share as outsider with "group1" and "user1" with same permissions
202
-			[
203
-				[
204
-					[1, 100, 'user2', '/share2', 31, null],
205
-				],
206
-				[
207
-					[2, 100, 'user2', '/share2', 31, null],
208
-				],
209
-				[
210
-					// combined, user share has higher priority
211
-					['1', 100, 'user2', '/share2', 31, []],
212
-				],
213
-			],
214
-			// #1: share as outsider with "group1" and "user1" with different permissions
215
-			[
216
-				[
217
-					[1, 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true], ['scope' => 'app', 'key' => 'attribute1', 'value' => true]]],
218
-				],
219
-				[
220
-					[2, 100, 'user2', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => false], ['scope' => 'app', 'key' => 'attribute2', 'value' => false]]],
221
-				],
222
-				[
223
-					// use highest permissions
224
-					['1', 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true], ['scope' => 'app', 'key' => 'attribute1', 'value' => true], ['scope' => 'app', 'key' => 'attribute2', 'value' => false]]],
225
-				],
226
-			],
227
-			// #2: share as outsider with "group1" and "group2" with same permissions
228
-			[
229
-				[
230
-				],
231
-				[
232
-					[1, 100, 'user2', '/share', 31, null],
233
-					[2, 100, 'user2', '/share', 31, []],
234
-				],
235
-				[
236
-					// combined, first group share has higher priority
237
-					['1', 100, 'user2', '/share', 31, []],
238
-				],
239
-			],
240
-			// #3: share as outsider with "group1" and "group2" with different permissions
241
-			[
242
-				[
243
-				],
244
-				[
245
-					[1, 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => false]]],
246
-					[2, 100, 'user2', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
247
-				],
248
-				[
249
-					// use higher permissions (most permissive)
250
-					['1', 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
251
-				],
252
-			],
253
-			// #4: share as insider with "group1"
254
-			[
255
-				[
256
-				],
257
-				[
258
-					[1, 100, 'user1', '/share', 31, []],
259
-				],
260
-				[
261
-					// no received share since "user1" is the sharer/owner
262
-				],
263
-			],
264
-			// #5: share as insider with "group1" and "group2" with different permissions
265
-			[
266
-				[
267
-				],
268
-				[
269
-					[1, 100, 'user1', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
270
-					[2, 100, 'user1', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => false]]],
271
-				],
272
-				[
273
-					// no received share since "user1" is the sharer/owner
274
-				],
275
-			],
276
-			// #6: share as outside with "group1", recipient opted out
277
-			[
278
-				[
279
-				],
280
-				[
281
-					[1, 100, 'user2', '/share', 0, []],
282
-				],
283
-				[
284
-					// no received share since "user1" opted out
285
-				],
286
-			],
287
-			// #7: share as outsider with "group1" and "user1" where recipient renamed in between
288
-			[
289
-				[
290
-					[1, 100, 'user2', '/share2-renamed', 31, []],
291
-				],
292
-				[
293
-					[2, 100, 'user2', '/share2', 31, []],
294
-				],
295
-				[
296
-					// use target of least recent share
297
-					['1', 100, 'user2', '/share2-renamed', 31, []],
298
-				],
299
-			],
300
-			// #8: share as outsider with "group1" and "user1" where recipient renamed in between
301
-			[
302
-				[
303
-					[2, 100, 'user2', '/share2', 31, []],
304
-				],
305
-				[
306
-					[1, 100, 'user2', '/share2-renamed', 31, []],
307
-				],
308
-				[
309
-					// use target of least recent share
310
-					['1', 100, 'user2', '/share2-renamed', 31, []],
311
-				],
312
-			],
313
-			// #9: share as outsider with "nullgroup" and "user1" where recipient renamed in between
314
-			[
315
-				[
316
-					[2, 100, 'user2', '/share2', 31, []],
317
-				],
318
-				[
319
-					[1, 100, 'nullgroup', '/share2-renamed', 31, []],
320
-				],
321
-				[
322
-					// use target of least recent share
323
-					['1', 100, 'nullgroup', '/share2-renamed', 31, []],
324
-				],
325
-				true
326
-			],
327
-		];
328
-	}
197
+    public static function mergeSharesDataProvider(): array {
198
+        // note: the user in the specs here is the shareOwner not recipient
199
+        // the recipient is always "user1"
200
+        return [
201
+            // #0: share as outsider with "group1" and "user1" with same permissions
202
+            [
203
+                [
204
+                    [1, 100, 'user2', '/share2', 31, null],
205
+                ],
206
+                [
207
+                    [2, 100, 'user2', '/share2', 31, null],
208
+                ],
209
+                [
210
+                    // combined, user share has higher priority
211
+                    ['1', 100, 'user2', '/share2', 31, []],
212
+                ],
213
+            ],
214
+            // #1: share as outsider with "group1" and "user1" with different permissions
215
+            [
216
+                [
217
+                    [1, 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true], ['scope' => 'app', 'key' => 'attribute1', 'value' => true]]],
218
+                ],
219
+                [
220
+                    [2, 100, 'user2', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => false], ['scope' => 'app', 'key' => 'attribute2', 'value' => false]]],
221
+                ],
222
+                [
223
+                    // use highest permissions
224
+                    ['1', 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true], ['scope' => 'app', 'key' => 'attribute1', 'value' => true], ['scope' => 'app', 'key' => 'attribute2', 'value' => false]]],
225
+                ],
226
+            ],
227
+            // #2: share as outsider with "group1" and "group2" with same permissions
228
+            [
229
+                [
230
+                ],
231
+                [
232
+                    [1, 100, 'user2', '/share', 31, null],
233
+                    [2, 100, 'user2', '/share', 31, []],
234
+                ],
235
+                [
236
+                    // combined, first group share has higher priority
237
+                    ['1', 100, 'user2', '/share', 31, []],
238
+                ],
239
+            ],
240
+            // #3: share as outsider with "group1" and "group2" with different permissions
241
+            [
242
+                [
243
+                ],
244
+                [
245
+                    [1, 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => false]]],
246
+                    [2, 100, 'user2', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
247
+                ],
248
+                [
249
+                    // use higher permissions (most permissive)
250
+                    ['1', 100, 'user2', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
251
+                ],
252
+            ],
253
+            // #4: share as insider with "group1"
254
+            [
255
+                [
256
+                ],
257
+                [
258
+                    [1, 100, 'user1', '/share', 31, []],
259
+                ],
260
+                [
261
+                    // no received share since "user1" is the sharer/owner
262
+                ],
263
+            ],
264
+            // #5: share as insider with "group1" and "group2" with different permissions
265
+            [
266
+                [
267
+                ],
268
+                [
269
+                    [1, 100, 'user1', '/share', 31, [['scope' => 'permission', 'key' => 'download', 'value' => true]]],
270
+                    [2, 100, 'user1', '/share', 15, [['scope' => 'permission', 'key' => 'download', 'value' => false]]],
271
+                ],
272
+                [
273
+                    // no received share since "user1" is the sharer/owner
274
+                ],
275
+            ],
276
+            // #6: share as outside with "group1", recipient opted out
277
+            [
278
+                [
279
+                ],
280
+                [
281
+                    [1, 100, 'user2', '/share', 0, []],
282
+                ],
283
+                [
284
+                    // no received share since "user1" opted out
285
+                ],
286
+            ],
287
+            // #7: share as outsider with "group1" and "user1" where recipient renamed in between
288
+            [
289
+                [
290
+                    [1, 100, 'user2', '/share2-renamed', 31, []],
291
+                ],
292
+                [
293
+                    [2, 100, 'user2', '/share2', 31, []],
294
+                ],
295
+                [
296
+                    // use target of least recent share
297
+                    ['1', 100, 'user2', '/share2-renamed', 31, []],
298
+                ],
299
+            ],
300
+            // #8: share as outsider with "group1" and "user1" where recipient renamed in between
301
+            [
302
+                [
303
+                    [2, 100, 'user2', '/share2', 31, []],
304
+                ],
305
+                [
306
+                    [1, 100, 'user2', '/share2-renamed', 31, []],
307
+                ],
308
+                [
309
+                    // use target of least recent share
310
+                    ['1', 100, 'user2', '/share2-renamed', 31, []],
311
+                ],
312
+            ],
313
+            // #9: share as outsider with "nullgroup" and "user1" where recipient renamed in between
314
+            [
315
+                [
316
+                    [2, 100, 'user2', '/share2', 31, []],
317
+                ],
318
+                [
319
+                    [1, 100, 'nullgroup', '/share2-renamed', 31, []],
320
+                ],
321
+                [
322
+                    // use target of least recent share
323
+                    ['1', 100, 'nullgroup', '/share2-renamed', 31, []],
324
+                ],
325
+                true
326
+            ],
327
+        ];
328
+    }
329 329
 
330
-	/**
331
-	 * Tests merging shares.
332
-	 *
333
-	 * Happens when sharing the same entry to a user through multiple ways,
334
-	 * like several groups and also direct shares at the same time.
335
-	 *
336
-	 *
337
-	 * @param array $userShares array of user share specs
338
-	 * @param array $groupShares array of group share specs
339
-	 * @param array $expectedShares array of expected supershare specs
340
-	 */
341
-	#[\PHPUnit\Framework\Attributes\DataProvider('mergeSharesDataProvider')]
342
-	public function testMergeShares($userShares, $groupShares, $expectedShares, $moveFails = false): void {
343
-		$rootFolder = $this->createMock(IRootFolder::class);
344
-		$userManager = $this->createMock(IUserManager::class);
330
+    /**
331
+     * Tests merging shares.
332
+     *
333
+     * Happens when sharing the same entry to a user through multiple ways,
334
+     * like several groups and also direct shares at the same time.
335
+     *
336
+     *
337
+     * @param array $userShares array of user share specs
338
+     * @param array $groupShares array of group share specs
339
+     * @param array $expectedShares array of expected supershare specs
340
+     */
341
+    #[\PHPUnit\Framework\Attributes\DataProvider('mergeSharesDataProvider')]
342
+    public function testMergeShares($userShares, $groupShares, $expectedShares, $moveFails = false): void {
343
+        $rootFolder = $this->createMock(IRootFolder::class);
344
+        $userManager = $this->createMock(IUserManager::class);
345 345
 
346
-		$userShares = array_map(function ($shareSpec) {
347
-			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
348
-		}, $userShares);
349
-		$groupShares = array_map(function ($shareSpec) {
350
-			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
351
-		}, $groupShares);
346
+        $userShares = array_map(function ($shareSpec) {
347
+            return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
348
+        }, $userShares);
349
+        $groupShares = array_map(function ($shareSpec) {
350
+            return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
351
+        }, $groupShares);
352 352
 
353
-		$this->user->expects($this->any())
354
-			->method('getUID')
355
-			->willReturn('user1');
353
+        $this->user->expects($this->any())
354
+            ->method('getUID')
355
+            ->willReturn('user1');
356 356
 
357
-		// tests regarding circles are made in the app itself.
358
-		$circleShares = [];
359
-		$roomShares = [];
360
-		$deckShares = [];
361
-		$scienceMeshShares = [];
362
-		$this->shareManager->expects($this->exactly(6))
363
-			->method('getSharedWith')
364
-			->willReturnMap([
365
-				['user1', IShare::TYPE_USER, null, -1, 0, $userShares],
366
-				['user1', IShare::TYPE_GROUP, null, -1, 0, $groupShares],
367
-				['user1', IShare::TYPE_CIRCLE, null, -1, 0, $circleShares],
368
-				['user1', IShare::TYPE_ROOM, null, -1, 0, $roomShares],
369
-				['user1', IShare::TYPE_DECK, null, -1, 0, $deckShares],
370
-				['user1', IShare::TYPE_SCIENCEMESH, null, -1, 0, $scienceMeshShares],
371
-			]);
357
+        // tests regarding circles are made in the app itself.
358
+        $circleShares = [];
359
+        $roomShares = [];
360
+        $deckShares = [];
361
+        $scienceMeshShares = [];
362
+        $this->shareManager->expects($this->exactly(6))
363
+            ->method('getSharedWith')
364
+            ->willReturnMap([
365
+                ['user1', IShare::TYPE_USER, null, -1, 0, $userShares],
366
+                ['user1', IShare::TYPE_GROUP, null, -1, 0, $groupShares],
367
+                ['user1', IShare::TYPE_CIRCLE, null, -1, 0, $circleShares],
368
+                ['user1', IShare::TYPE_ROOM, null, -1, 0, $roomShares],
369
+                ['user1', IShare::TYPE_DECK, null, -1, 0, $deckShares],
370
+                ['user1', IShare::TYPE_SCIENCEMESH, null, -1, 0, $scienceMeshShares],
371
+            ]);
372 372
 
373
-		$this->shareManager->expects($this->any())
374
-			->method('newShare')
375
-			->willReturnCallback(function () use ($rootFolder, $userManager) {
376
-				return new Share($rootFolder, $userManager);
377
-			});
373
+        $this->shareManager->expects($this->any())
374
+            ->method('newShare')
375
+            ->willReturnCallback(function () use ($rootFolder, $userManager) {
376
+                return new Share($rootFolder, $userManager);
377
+            });
378 378
 
379
-		if ($moveFails) {
380
-			$this->shareManager->expects($this->any())
381
-				->method('moveShare')
382
-				->willThrowException(new \InvalidArgumentException());
383
-		}
379
+        if ($moveFails) {
380
+            $this->shareManager->expects($this->any())
381
+                ->method('moveShare')
382
+                ->willThrowException(new \InvalidArgumentException());
383
+        }
384 384
 
385
-		$mounts = $this->provider->getMountsForUser($this->user, $this->loader);
385
+        $mounts = $this->provider->getMountsForUser($this->user, $this->loader);
386 386
 
387
-		$this->assertCount(count($expectedShares), $mounts);
387
+        $this->assertCount(count($expectedShares), $mounts);
388 388
 
389
-		foreach ($mounts as $index => $mount) {
390
-			$expectedShare = $expectedShares[$index];
391
-			$this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mount);
389
+        foreach ($mounts as $index => $mount) {
390
+            $expectedShare = $expectedShares[$index];
391
+            $this->assertInstanceOf('OCA\Files_Sharing\SharedMount', $mount);
392 392
 
393
-			// supershare
394
-			/** @var SharedMount $mount */
395
-			$share = $mount->getShare();
393
+            // supershare
394
+            /** @var SharedMount $mount */
395
+            $share = $mount->getShare();
396 396
 
397
-			$this->assertEquals($expectedShare[0], $share->getId());
398
-			$this->assertEquals($expectedShare[1], $share->getNodeId());
399
-			$this->assertEquals($expectedShare[2], $share->getShareOwner());
400
-			$this->assertEquals($expectedShare[3], $share->getTarget());
401
-			$this->assertEquals($expectedShare[4], $share->getPermissions());
402
-			if ($expectedShare[5] === null) {
403
-				$this->assertNull($share->getAttributes());
404
-			} else {
405
-				$this->assertEquals($expectedShare[5], $share->getAttributes()->toArray());
406
-			}
407
-		}
408
-	}
397
+            $this->assertEquals($expectedShare[0], $share->getId());
398
+            $this->assertEquals($expectedShare[1], $share->getNodeId());
399
+            $this->assertEquals($expectedShare[2], $share->getShareOwner());
400
+            $this->assertEquals($expectedShare[3], $share->getTarget());
401
+            $this->assertEquals($expectedShare[4], $share->getPermissions());
402
+            if ($expectedShare[5] === null) {
403
+                $this->assertNull($share->getAttributes());
404
+            } else {
405
+                $this->assertEquals($expectedShare[5], $share->getAttributes()->toArray());
406
+            }
407
+        }
408
+    }
409 409
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
 		$shareAttributes = $this->createMock(IShareAttributes::class);
64 64
 		$shareAttributes->method('toArray')->willReturn($attrs);
65 65
 		$shareAttributes->method('getAttribute')->willReturnCallback(
66
-			function ($scope, $key) use ($attrs) {
66
+			function($scope, $key) use ($attrs) {
67 67
 				$result = null;
68 68
 				foreach ($attrs as $attr) {
69 69
 					if ($attr['key'] === $key && $attr['scope'] === $scope) {
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 			->method('getShareTime')
101 101
 			->willReturn(
102 102
 				// compute share time based on id, simulating share order
103
-				new \DateTime('@' . (1469193980 + 1000 * $id))
103
+				new \DateTime('@'.(1469193980 + 1000 * $id))
104 104
 			);
105 105
 		return $share;
106 106
 	}
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 
156 156
 		$this->shareManager->expects($this->any())
157 157
 			->method('newShare')
158
-			->willReturnCallback(function () use ($rootFolder, $userManager) {
158
+			->willReturnCallback(function() use ($rootFolder, $userManager) {
159 159
 				return new Share($rootFolder, $userManager);
160 160
 			});
161 161
 
@@ -343,10 +343,10 @@  discard block
 block discarded – undo
343 343
 		$rootFolder = $this->createMock(IRootFolder::class);
344 344
 		$userManager = $this->createMock(IUserManager::class);
345 345
 
346
-		$userShares = array_map(function ($shareSpec) {
346
+		$userShares = array_map(function($shareSpec) {
347 347
 			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
348 348
 		}, $userShares);
349
-		$groupShares = array_map(function ($shareSpec) {
349
+		$groupShares = array_map(function($shareSpec) {
350 350
 			return $this->makeMockShare($shareSpec[0], $shareSpec[1], $shareSpec[2], $shareSpec[3], $shareSpec[4], $shareSpec[5]);
351 351
 		}, $groupShares);
352 352
 
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
 
373 373
 		$this->shareManager->expects($this->any())
374 374
 			->method('newShare')
375
-			->willReturnCallback(function () use ($rootFolder, $userManager) {
375
+			->willReturnCallback(function() use ($rootFolder, $userManager) {
376 376
 				return new Share($rootFolder, $userManager);
377 377
 			});
378 378
 
Please login to merge, or discard this patch.
apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php 1 patch
Indentation   +215 added lines, -215 removed lines patch added patch discarded remove patch
@@ -16,219 +16,219 @@
 block discarded – undo
16 16
 use Test\TestCase;
17 17
 
18 18
 class ShareRecipientSorterTest extends TestCase {
19
-	/** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
20
-	protected $shareManager;
21
-	/** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject */
22
-	protected $rootFolder;
23
-	/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
24
-	protected $userSession;
25
-	/** @var ShareRecipientSorter */
26
-	protected $sorter;
27
-
28
-	protected function setUp(): void {
29
-		parent::setUp();
30
-
31
-		$this->shareManager = $this->createMock(IManager::class);
32
-		$this->rootFolder = $this->createMock(IRootFolder::class);
33
-		$this->userSession = $this->createMock(IUserSession::class);
34
-
35
-		$this->sorter = new ShareRecipientSorter($this->shareManager, $this->rootFolder, $this->userSession);
36
-	}
37
-
38
-	/**
39
-	 * @param $data
40
-	 */
41
-	#[\PHPUnit\Framework\Attributes\DataProvider('sortDataProvider')]
42
-	public function testSort($data): void {
43
-		$node = $this->createMock(Node::class);
44
-
45
-		/** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */
46
-		$folder = $this->createMock(Folder::class);
47
-		$this->rootFolder->expects($this->any())
48
-			->method('getUserFolder')
49
-			->willReturn($folder);
50
-
51
-		$user = $this->createMock(IUser::class);
52
-		$user->expects($this->any())
53
-			->method('getUID')
54
-			->willReturn('yvonne');
55
-
56
-		$this->userSession->expects($this->once())
57
-			->method('getUser')
58
-			->willReturn($user);
59
-
60
-		if ($data['context']['itemType'] === 'files') {
61
-			$folder->expects($this->once())
62
-				->method('getFirstNodeById')
63
-				->with($data['context']['itemId'])
64
-				->willReturn($node);
65
-
66
-			$this->shareManager->expects($this->once())
67
-				->method('getAccessList')
68
-				->with($node)
69
-				->willReturn($data['accessList']);
70
-		} else {
71
-			$folder->expects($this->never())
72
-				->method('getFirstNodeById');
73
-			$this->shareManager->expects($this->never())
74
-				->method('getAccessList');
75
-		}
76
-
77
-		$workArray = $data['input'];
78
-		$this->sorter->sort($workArray, $data['context']);
79
-
80
-		$this->assertEquals($data['expected'], $workArray);
81
-	}
82
-
83
-	public function testSortNoNodes(): void {
84
-		/** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */
85
-		$folder = $this->createMock(Folder::class);
86
-		$this->rootFolder->expects($this->any())
87
-			->method('getUserFolder')
88
-			->willReturn($folder);
89
-
90
-		$folder->expects($this->once())
91
-			->method('getFirstNodeById')
92
-			->willReturn(null);
93
-
94
-		$user = $this->createMock(IUser::class);
95
-		$user->expects($this->any())
96
-			->method('getUID')
97
-			->willReturn('yvonne');
98
-
99
-		$this->userSession->expects($this->once())
100
-			->method('getUser')
101
-			->willReturn($user);
102
-
103
-		$this->shareManager->expects($this->never())
104
-			->method('getAccessList');
105
-
106
-		$originalArray = [
107
-			'users' => [
108
-				['value' => ['shareWith' => 'alice']],
109
-				['value' => ['shareWith' => 'bob']],
110
-			]
111
-		];
112
-		$workArray = $originalArray;
113
-		$this->sorter->sort($workArray, ['itemType' => 'files', 'itemId' => '404']);
114
-
115
-		$this->assertEquals($originalArray, $workArray);
116
-	}
117
-
118
-	public static function sortDataProvider() {
119
-		return [[
120
-			[
121
-				#0 – sort properly and otherwise keep existing order
122
-				'context' => ['itemType' => 'files', 'itemId' => '42'],
123
-				'accessList' => ['users' => ['celia', 'darius', 'faruk', 'gail'], 'bots' => ['r2-d2']],
124
-				'input' => [
125
-					'users' => [
126
-						['value' => ['shareWith' => 'alice']],
127
-						['value' => ['shareWith' => 'bob']],
128
-						['value' => ['shareWith' => 'celia']],
129
-						['value' => ['shareWith' => 'darius']],
130
-						['value' => ['shareWith' => 'elena']],
131
-						['value' => ['shareWith' => 'faruk']],
132
-						['value' => ['shareWith' => 'gail']],
133
-					],
134
-					'bots' => [
135
-						['value' => ['shareWith' => 'c-3po']],
136
-						['value' => ['shareWith' => 'r2-d2']],
137
-					]
138
-				],
139
-				'expected' => [
140
-					'users' => [
141
-						['value' => ['shareWith' => 'celia']],
142
-						['value' => ['shareWith' => 'darius']],
143
-						['value' => ['shareWith' => 'faruk']],
144
-						['value' => ['shareWith' => 'gail']],
145
-						['value' => ['shareWith' => 'alice']],
146
-						['value' => ['shareWith' => 'bob']],
147
-						['value' => ['shareWith' => 'elena']],
148
-					],
149
-					'bots' => [
150
-						['value' => ['shareWith' => 'r2-d2']],
151
-						['value' => ['shareWith' => 'c-3po']],
152
-					]
153
-				],
154
-			],
155
-			[
156
-				#1 – no recipients
157
-				'context' => ['itemType' => 'files', 'itemId' => '42'],
158
-				'accessList' => ['users' => false],
159
-				'input' => [
160
-					'users' => [
161
-						['value' => ['shareWith' => 'alice']],
162
-						['value' => ['shareWith' => 'bob']],
163
-						['value' => ['shareWith' => 'celia']],
164
-						['value' => ['shareWith' => 'darius']],
165
-						['value' => ['shareWith' => 'elena']],
166
-						['value' => ['shareWith' => 'faruk']],
167
-						['value' => ['shareWith' => 'gail']],
168
-					],
169
-					'bots' => [
170
-						['value' => ['shareWith' => 'c-3po']],
171
-						['value' => ['shareWith' => 'r2-d2']],
172
-					]
173
-				],
174
-				'expected' => [
175
-					'users' => [
176
-						['value' => ['shareWith' => 'alice']],
177
-						['value' => ['shareWith' => 'bob']],
178
-						['value' => ['shareWith' => 'celia']],
179
-						['value' => ['shareWith' => 'darius']],
180
-						['value' => ['shareWith' => 'elena']],
181
-						['value' => ['shareWith' => 'faruk']],
182
-						['value' => ['shareWith' => 'gail']],
183
-					],
184
-					'bots' => [
185
-						['value' => ['shareWith' => 'c-3po']],
186
-						['value' => ['shareWith' => 'r2-d2']],
187
-					]
188
-				],
189
-			],
190
-			[
191
-				#2 – unsupported item  type
192
-				'context' => ['itemType' => 'announcements', 'itemId' => '42'],
193
-				'accessList' => null, // not needed
194
-				'input' => [
195
-					'users' => [
196
-						['value' => ['shareWith' => 'alice']],
197
-						['value' => ['shareWith' => 'bob']],
198
-						['value' => ['shareWith' => 'celia']],
199
-						['value' => ['shareWith' => 'darius']],
200
-						['value' => ['shareWith' => 'elena']],
201
-						['value' => ['shareWith' => 'faruk']],
202
-						['value' => ['shareWith' => 'gail']],
203
-					],
204
-					'bots' => [
205
-						['value' => ['shareWith' => 'c-3po']],
206
-						['value' => ['shareWith' => 'r2-d2']],
207
-					]
208
-				],
209
-				'expected' => [
210
-					'users' => [
211
-						['value' => ['shareWith' => 'alice']],
212
-						['value' => ['shareWith' => 'bob']],
213
-						['value' => ['shareWith' => 'celia']],
214
-						['value' => ['shareWith' => 'darius']],
215
-						['value' => ['shareWith' => 'elena']],
216
-						['value' => ['shareWith' => 'faruk']],
217
-						['value' => ['shareWith' => 'gail']],
218
-					],
219
-					'bots' => [
220
-						['value' => ['shareWith' => 'c-3po']],
221
-						['value' => ['shareWith' => 'r2-d2']],
222
-					]
223
-				],
224
-			],
225
-			[
226
-				#3 – no nothing
227
-				'context' => ['itemType' => 'files', 'itemId' => '42'],
228
-				'accessList' => [],
229
-				'input' => [],
230
-				'expected' => [],
231
-			],
232
-		]];
233
-	}
19
+    /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
20
+    protected $shareManager;
21
+    /** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject */
22
+    protected $rootFolder;
23
+    /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
24
+    protected $userSession;
25
+    /** @var ShareRecipientSorter */
26
+    protected $sorter;
27
+
28
+    protected function setUp(): void {
29
+        parent::setUp();
30
+
31
+        $this->shareManager = $this->createMock(IManager::class);
32
+        $this->rootFolder = $this->createMock(IRootFolder::class);
33
+        $this->userSession = $this->createMock(IUserSession::class);
34
+
35
+        $this->sorter = new ShareRecipientSorter($this->shareManager, $this->rootFolder, $this->userSession);
36
+    }
37
+
38
+    /**
39
+     * @param $data
40
+     */
41
+    #[\PHPUnit\Framework\Attributes\DataProvider('sortDataProvider')]
42
+    public function testSort($data): void {
43
+        $node = $this->createMock(Node::class);
44
+
45
+        /** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */
46
+        $folder = $this->createMock(Folder::class);
47
+        $this->rootFolder->expects($this->any())
48
+            ->method('getUserFolder')
49
+            ->willReturn($folder);
50
+
51
+        $user = $this->createMock(IUser::class);
52
+        $user->expects($this->any())
53
+            ->method('getUID')
54
+            ->willReturn('yvonne');
55
+
56
+        $this->userSession->expects($this->once())
57
+            ->method('getUser')
58
+            ->willReturn($user);
59
+
60
+        if ($data['context']['itemType'] === 'files') {
61
+            $folder->expects($this->once())
62
+                ->method('getFirstNodeById')
63
+                ->with($data['context']['itemId'])
64
+                ->willReturn($node);
65
+
66
+            $this->shareManager->expects($this->once())
67
+                ->method('getAccessList')
68
+                ->with($node)
69
+                ->willReturn($data['accessList']);
70
+        } else {
71
+            $folder->expects($this->never())
72
+                ->method('getFirstNodeById');
73
+            $this->shareManager->expects($this->never())
74
+                ->method('getAccessList');
75
+        }
76
+
77
+        $workArray = $data['input'];
78
+        $this->sorter->sort($workArray, $data['context']);
79
+
80
+        $this->assertEquals($data['expected'], $workArray);
81
+    }
82
+
83
+    public function testSortNoNodes(): void {
84
+        /** @var Folder|\PHPUnit\Framework\MockObject\MockObject $folder */
85
+        $folder = $this->createMock(Folder::class);
86
+        $this->rootFolder->expects($this->any())
87
+            ->method('getUserFolder')
88
+            ->willReturn($folder);
89
+
90
+        $folder->expects($this->once())
91
+            ->method('getFirstNodeById')
92
+            ->willReturn(null);
93
+
94
+        $user = $this->createMock(IUser::class);
95
+        $user->expects($this->any())
96
+            ->method('getUID')
97
+            ->willReturn('yvonne');
98
+
99
+        $this->userSession->expects($this->once())
100
+            ->method('getUser')
101
+            ->willReturn($user);
102
+
103
+        $this->shareManager->expects($this->never())
104
+            ->method('getAccessList');
105
+
106
+        $originalArray = [
107
+            'users' => [
108
+                ['value' => ['shareWith' => 'alice']],
109
+                ['value' => ['shareWith' => 'bob']],
110
+            ]
111
+        ];
112
+        $workArray = $originalArray;
113
+        $this->sorter->sort($workArray, ['itemType' => 'files', 'itemId' => '404']);
114
+
115
+        $this->assertEquals($originalArray, $workArray);
116
+    }
117
+
118
+    public static function sortDataProvider() {
119
+        return [[
120
+            [
121
+                #0 – sort properly and otherwise keep existing order
122
+                'context' => ['itemType' => 'files', 'itemId' => '42'],
123
+                'accessList' => ['users' => ['celia', 'darius', 'faruk', 'gail'], 'bots' => ['r2-d2']],
124
+                'input' => [
125
+                    'users' => [
126
+                        ['value' => ['shareWith' => 'alice']],
127
+                        ['value' => ['shareWith' => 'bob']],
128
+                        ['value' => ['shareWith' => 'celia']],
129
+                        ['value' => ['shareWith' => 'darius']],
130
+                        ['value' => ['shareWith' => 'elena']],
131
+                        ['value' => ['shareWith' => 'faruk']],
132
+                        ['value' => ['shareWith' => 'gail']],
133
+                    ],
134
+                    'bots' => [
135
+                        ['value' => ['shareWith' => 'c-3po']],
136
+                        ['value' => ['shareWith' => 'r2-d2']],
137
+                    ]
138
+                ],
139
+                'expected' => [
140
+                    'users' => [
141
+                        ['value' => ['shareWith' => 'celia']],
142
+                        ['value' => ['shareWith' => 'darius']],
143
+                        ['value' => ['shareWith' => 'faruk']],
144
+                        ['value' => ['shareWith' => 'gail']],
145
+                        ['value' => ['shareWith' => 'alice']],
146
+                        ['value' => ['shareWith' => 'bob']],
147
+                        ['value' => ['shareWith' => 'elena']],
148
+                    ],
149
+                    'bots' => [
150
+                        ['value' => ['shareWith' => 'r2-d2']],
151
+                        ['value' => ['shareWith' => 'c-3po']],
152
+                    ]
153
+                ],
154
+            ],
155
+            [
156
+                #1 – no recipients
157
+                'context' => ['itemType' => 'files', 'itemId' => '42'],
158
+                'accessList' => ['users' => false],
159
+                'input' => [
160
+                    'users' => [
161
+                        ['value' => ['shareWith' => 'alice']],
162
+                        ['value' => ['shareWith' => 'bob']],
163
+                        ['value' => ['shareWith' => 'celia']],
164
+                        ['value' => ['shareWith' => 'darius']],
165
+                        ['value' => ['shareWith' => 'elena']],
166
+                        ['value' => ['shareWith' => 'faruk']],
167
+                        ['value' => ['shareWith' => 'gail']],
168
+                    ],
169
+                    'bots' => [
170
+                        ['value' => ['shareWith' => 'c-3po']],
171
+                        ['value' => ['shareWith' => 'r2-d2']],
172
+                    ]
173
+                ],
174
+                'expected' => [
175
+                    'users' => [
176
+                        ['value' => ['shareWith' => 'alice']],
177
+                        ['value' => ['shareWith' => 'bob']],
178
+                        ['value' => ['shareWith' => 'celia']],
179
+                        ['value' => ['shareWith' => 'darius']],
180
+                        ['value' => ['shareWith' => 'elena']],
181
+                        ['value' => ['shareWith' => 'faruk']],
182
+                        ['value' => ['shareWith' => 'gail']],
183
+                    ],
184
+                    'bots' => [
185
+                        ['value' => ['shareWith' => 'c-3po']],
186
+                        ['value' => ['shareWith' => 'r2-d2']],
187
+                    ]
188
+                ],
189
+            ],
190
+            [
191
+                #2 – unsupported item  type
192
+                'context' => ['itemType' => 'announcements', 'itemId' => '42'],
193
+                'accessList' => null, // not needed
194
+                'input' => [
195
+                    'users' => [
196
+                        ['value' => ['shareWith' => 'alice']],
197
+                        ['value' => ['shareWith' => 'bob']],
198
+                        ['value' => ['shareWith' => 'celia']],
199
+                        ['value' => ['shareWith' => 'darius']],
200
+                        ['value' => ['shareWith' => 'elena']],
201
+                        ['value' => ['shareWith' => 'faruk']],
202
+                        ['value' => ['shareWith' => 'gail']],
203
+                    ],
204
+                    'bots' => [
205
+                        ['value' => ['shareWith' => 'c-3po']],
206
+                        ['value' => ['shareWith' => 'r2-d2']],
207
+                    ]
208
+                ],
209
+                'expected' => [
210
+                    'users' => [
211
+                        ['value' => ['shareWith' => 'alice']],
212
+                        ['value' => ['shareWith' => 'bob']],
213
+                        ['value' => ['shareWith' => 'celia']],
214
+                        ['value' => ['shareWith' => 'darius']],
215
+                        ['value' => ['shareWith' => 'elena']],
216
+                        ['value' => ['shareWith' => 'faruk']],
217
+                        ['value' => ['shareWith' => 'gail']],
218
+                    ],
219
+                    'bots' => [
220
+                        ['value' => ['shareWith' => 'c-3po']],
221
+                        ['value' => ['shareWith' => 'r2-d2']],
222
+                    ]
223
+                ],
224
+            ],
225
+            [
226
+                #3 – no nothing
227
+                'context' => ['itemType' => 'files', 'itemId' => '42'],
228
+                'accessList' => [],
229
+                'input' => [],
230
+                'expected' => [],
231
+            ],
232
+        ]];
233
+    }
234 234
 }
Please login to merge, or discard this patch.
apps/files_sharing/tests/Command/CleanupRemoteStoragesTest.php 2 patches
Indentation   +176 added lines, -176 removed lines patch added patch discarded remove patch
@@ -26,180 +26,180 @@
 block discarded – undo
26 26
  */
27 27
 class CleanupRemoteStoragesTest extends TestCase {
28 28
 
29
-	protected IDBConnection $connection;
30
-	protected CleanupRemoteStorages $command;
31
-	private ICloudIdManager&MockObject $cloudIdManager;
32
-
33
-	private $storages = [
34
-		['id' => 'shared::7b4a322b22f9d0047c38d77d471ce3cf', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e1', 'remote' => 'https://hostname.tld/owncloud1', 'user' => 'user1'],
35
-		['id' => 'shared::efe3b456112c3780da6155d3a9b9141c', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e2', 'remote' => 'https://hostname.tld/owncloud2', 'user' => 'user2'],
36
-		['notExistingId' => 'shared::33323d9f4ca416a9e3525b435354bc6f', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e3', 'remote' => 'https://hostname.tld/owncloud3', 'user' => 'user3'],
37
-		['id' => 'shared::7fe41a07d3f517a923f4b2b599e72cbb', 'files_count' => 2],
38
-		['id' => 'shared::de4aeb2f378d222b6d2c5fd8f4e42f8e', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e5', 'remote' => 'https://hostname.tld/owncloud5', 'user' => 'user5'],
39
-		['id' => 'shared::af712293ab5eb9e6a1745a13818b99fe', 'files_count' => 3],
40
-		['notExistingId' => 'shared::c34568c143cdac7d2f06e0800b5280f9', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e7', 'remote' => 'https://hostname.tld/owncloud7', 'user' => 'user7'],
41
-	];
42
-
43
-	protected function setUp(): void {
44
-		parent::setUp();
45
-
46
-		$this->connection = Server::get(IDBConnection::class);
47
-
48
-		$storageQuery = Server::get(IDBConnection::class)->getQueryBuilder();
49
-		$storageQuery->insert('storages')
50
-			->setValue('id', $storageQuery->createParameter('id'));
51
-
52
-		$shareExternalQuery = Server::get(IDBConnection::class)->getQueryBuilder();
53
-		$shareExternalQuery->insert('share_external')
54
-			->setValue('share_token', $shareExternalQuery->createParameter('share_token'))
55
-			->setValue('remote', $shareExternalQuery->createParameter('remote'))
56
-			->setValue('name', $shareExternalQuery->createParameter('name'))
57
-			->setValue('owner', $shareExternalQuery->createParameter('owner'))
58
-			->setValue('user', $shareExternalQuery->createParameter('user'))
59
-			->setValue('mountpoint', $shareExternalQuery->createParameter('mountpoint'))
60
-			->setValue('mountpoint_hash', $shareExternalQuery->createParameter('mountpoint_hash'));
61
-
62
-		$filesQuery = Server::get(IDBConnection::class)->getQueryBuilder();
63
-		$filesQuery->insert('filecache')
64
-			->setValue('storage', $filesQuery->createParameter('storage'))
65
-			->setValue('path', $filesQuery->createParameter('path'))
66
-			->setValue('path_hash', $filesQuery->createParameter('path_hash'));
67
-
68
-		foreach ($this->storages as &$storage) {
69
-			if (isset($storage['id'])) {
70
-				$storageQuery->setParameter('id', $storage['id']);
71
-				$storageQuery->executeStatement();
72
-				$storage['numeric_id'] = $storageQuery->getLastInsertId();
73
-			}
74
-
75
-			if (isset($storage['share_token'])) {
76
-				$shareExternalQuery
77
-					->setParameter('share_token', $storage['share_token'])
78
-					->setParameter('remote', $storage['remote'])
79
-					->setParameter('name', 'irrelevant')
80
-					->setParameter('owner', 'irrelevant')
81
-					->setParameter('user', $storage['user'])
82
-					->setParameter('mountpoint', 'irrelevant')
83
-					->setParameter('mountpoint_hash', 'irrelevant');
84
-				$shareExternalQuery->executeStatement();
85
-			}
86
-
87
-			if (isset($storage['files_count'])) {
88
-				for ($i = 0; $i < $storage['files_count']; $i++) {
89
-					$filesQuery->setParameter('storage', $storage['numeric_id']);
90
-					$filesQuery->setParameter('path', 'file' . $i);
91
-					$filesQuery->setParameter('path_hash', md5('file' . $i));
92
-					$filesQuery->executeStatement();
93
-				}
94
-			}
95
-		}
96
-
97
-		$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
98
-
99
-		$this->command = new CleanupRemoteStorages($this->connection, $this->cloudIdManager);
100
-	}
101
-
102
-	protected function tearDown(): void {
103
-		$storageQuery = Server::get(IDBConnection::class)->getQueryBuilder();
104
-		$storageQuery->delete('storages')
105
-			->where($storageQuery->expr()->eq('id', $storageQuery->createParameter('id')));
106
-
107
-		$shareExternalQuery = Server::get(IDBConnection::class)->getQueryBuilder();
108
-		$shareExternalQuery->delete('share_external')
109
-			->where($shareExternalQuery->expr()->eq('share_token', $shareExternalQuery->createParameter('share_token')))
110
-			->andWhere($shareExternalQuery->expr()->eq('remote', $shareExternalQuery->createParameter('remote')));
111
-
112
-		foreach ($this->storages as $storage) {
113
-			if (isset($storage['id'])) {
114
-				$storageQuery->setParameter('id', $storage['id']);
115
-				$storageQuery->executeStatement();
116
-			}
117
-
118
-			if (isset($storage['share_token'])) {
119
-				$shareExternalQuery->setParameter('share_token', $storage['share_token']);
120
-				$shareExternalQuery->setParameter('remote', $storage['remote']);
121
-				$shareExternalQuery->executeStatement();
122
-			}
123
-		}
124
-
125
-		parent::tearDown();
126
-	}
127
-
128
-	private function doesStorageExist($numericId) {
129
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
130
-		$qb->select('*')
131
-			->from('storages')
132
-			->where($qb->expr()->eq('numeric_id', $qb->createNamedParameter($numericId)));
133
-
134
-		$qResult = $qb->executeQuery();
135
-		$result = $qResult->fetch();
136
-		$qResult->closeCursor();
137
-		if (!empty($result)) {
138
-			return true;
139
-		}
140
-
141
-		$qb = Server::get(IDBConnection::class)->getQueryBuilder();
142
-		$qb->select('*')
143
-			->from('filecache')
144
-			->where($qb->expr()->eq('storage', $qb->createNamedParameter($numericId)));
145
-
146
-		$qResult = $qb->executeQuery();
147
-		$result = $qResult->fetch();
148
-		$qResult->closeCursor();
149
-		if (!empty($result)) {
150
-			return true;
151
-		}
152
-
153
-		return false;
154
-	}
155
-
156
-	/**
157
-	 * Test cleanup of orphaned storages
158
-	 */
159
-	public function testCleanup(): void {
160
-		$input = $this->getMockBuilder(InputInterface::class)
161
-			->disableOriginalConstructor()
162
-			->getMock();
163
-		$output = $this->getMockBuilder(OutputInterface::class)
164
-			->disableOriginalConstructor()
165
-			->getMock();
166
-
167
-		// parent folder, `files`, ´test` and `welcome.txt` => 4 elements
168
-		$outputCalls = [];
169
-		$output
170
-			->expects($this->any())
171
-			->method('writeln')
172
-			->willReturnCallback(function (string $text) use (&$outputCalls): void {
173
-				$outputCalls[] = $text;
174
-			});
175
-
176
-		$this->cloudIdManager
177
-			->expects($this->any())
178
-			->method('getCloudId')
179
-			->willReturnCallback(function (string $user, string $remote) {
180
-				$cloudIdMock = $this->createMock(ICloudId::class);
181
-
182
-				// The remotes are already sanitized in the original data, so
183
-				// they can be directly returned.
184
-				$cloudIdMock
185
-					->expects($this->any())
186
-					->method('getRemote')
187
-					->willReturn($remote);
188
-
189
-				return $cloudIdMock;
190
-			});
191
-
192
-		$this->command->execute($input, $output);
193
-
194
-		$this->assertTrue($this->doesStorageExist($this->storages[0]['numeric_id']));
195
-		$this->assertTrue($this->doesStorageExist($this->storages[1]['numeric_id']));
196
-		$this->assertFalse($this->doesStorageExist($this->storages[3]['numeric_id']));
197
-		$this->assertTrue($this->doesStorageExist($this->storages[4]['numeric_id']));
198
-		$this->assertFalse($this->doesStorageExist($this->storages[5]['numeric_id']));
199
-
200
-		$this->assertEquals([
201
-			'5 remote storage(s) need(s) to be checked',
202
-			'5 remote share(s) exist',
203
-		], array_slice($outputCalls, 0, 2));
204
-	}
29
+    protected IDBConnection $connection;
30
+    protected CleanupRemoteStorages $command;
31
+    private ICloudIdManager&MockObject $cloudIdManager;
32
+
33
+    private $storages = [
34
+        ['id' => 'shared::7b4a322b22f9d0047c38d77d471ce3cf', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e1', 'remote' => 'https://hostname.tld/owncloud1', 'user' => 'user1'],
35
+        ['id' => 'shared::efe3b456112c3780da6155d3a9b9141c', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e2', 'remote' => 'https://hostname.tld/owncloud2', 'user' => 'user2'],
36
+        ['notExistingId' => 'shared::33323d9f4ca416a9e3525b435354bc6f', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e3', 'remote' => 'https://hostname.tld/owncloud3', 'user' => 'user3'],
37
+        ['id' => 'shared::7fe41a07d3f517a923f4b2b599e72cbb', 'files_count' => 2],
38
+        ['id' => 'shared::de4aeb2f378d222b6d2c5fd8f4e42f8e', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e5', 'remote' => 'https://hostname.tld/owncloud5', 'user' => 'user5'],
39
+        ['id' => 'shared::af712293ab5eb9e6a1745a13818b99fe', 'files_count' => 3],
40
+        ['notExistingId' => 'shared::c34568c143cdac7d2f06e0800b5280f9', 'share_token' => 'f2c69dad1dc0649f26976fd210fc62e7', 'remote' => 'https://hostname.tld/owncloud7', 'user' => 'user7'],
41
+    ];
42
+
43
+    protected function setUp(): void {
44
+        parent::setUp();
45
+
46
+        $this->connection = Server::get(IDBConnection::class);
47
+
48
+        $storageQuery = Server::get(IDBConnection::class)->getQueryBuilder();
49
+        $storageQuery->insert('storages')
50
+            ->setValue('id', $storageQuery->createParameter('id'));
51
+
52
+        $shareExternalQuery = Server::get(IDBConnection::class)->getQueryBuilder();
53
+        $shareExternalQuery->insert('share_external')
54
+            ->setValue('share_token', $shareExternalQuery->createParameter('share_token'))
55
+            ->setValue('remote', $shareExternalQuery->createParameter('remote'))
56
+            ->setValue('name', $shareExternalQuery->createParameter('name'))
57
+            ->setValue('owner', $shareExternalQuery->createParameter('owner'))
58
+            ->setValue('user', $shareExternalQuery->createParameter('user'))
59
+            ->setValue('mountpoint', $shareExternalQuery->createParameter('mountpoint'))
60
+            ->setValue('mountpoint_hash', $shareExternalQuery->createParameter('mountpoint_hash'));
61
+
62
+        $filesQuery = Server::get(IDBConnection::class)->getQueryBuilder();
63
+        $filesQuery->insert('filecache')
64
+            ->setValue('storage', $filesQuery->createParameter('storage'))
65
+            ->setValue('path', $filesQuery->createParameter('path'))
66
+            ->setValue('path_hash', $filesQuery->createParameter('path_hash'));
67
+
68
+        foreach ($this->storages as &$storage) {
69
+            if (isset($storage['id'])) {
70
+                $storageQuery->setParameter('id', $storage['id']);
71
+                $storageQuery->executeStatement();
72
+                $storage['numeric_id'] = $storageQuery->getLastInsertId();
73
+            }
74
+
75
+            if (isset($storage['share_token'])) {
76
+                $shareExternalQuery
77
+                    ->setParameter('share_token', $storage['share_token'])
78
+                    ->setParameter('remote', $storage['remote'])
79
+                    ->setParameter('name', 'irrelevant')
80
+                    ->setParameter('owner', 'irrelevant')
81
+                    ->setParameter('user', $storage['user'])
82
+                    ->setParameter('mountpoint', 'irrelevant')
83
+                    ->setParameter('mountpoint_hash', 'irrelevant');
84
+                $shareExternalQuery->executeStatement();
85
+            }
86
+
87
+            if (isset($storage['files_count'])) {
88
+                for ($i = 0; $i < $storage['files_count']; $i++) {
89
+                    $filesQuery->setParameter('storage', $storage['numeric_id']);
90
+                    $filesQuery->setParameter('path', 'file' . $i);
91
+                    $filesQuery->setParameter('path_hash', md5('file' . $i));
92
+                    $filesQuery->executeStatement();
93
+                }
94
+            }
95
+        }
96
+
97
+        $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
98
+
99
+        $this->command = new CleanupRemoteStorages($this->connection, $this->cloudIdManager);
100
+    }
101
+
102
+    protected function tearDown(): void {
103
+        $storageQuery = Server::get(IDBConnection::class)->getQueryBuilder();
104
+        $storageQuery->delete('storages')
105
+            ->where($storageQuery->expr()->eq('id', $storageQuery->createParameter('id')));
106
+
107
+        $shareExternalQuery = Server::get(IDBConnection::class)->getQueryBuilder();
108
+        $shareExternalQuery->delete('share_external')
109
+            ->where($shareExternalQuery->expr()->eq('share_token', $shareExternalQuery->createParameter('share_token')))
110
+            ->andWhere($shareExternalQuery->expr()->eq('remote', $shareExternalQuery->createParameter('remote')));
111
+
112
+        foreach ($this->storages as $storage) {
113
+            if (isset($storage['id'])) {
114
+                $storageQuery->setParameter('id', $storage['id']);
115
+                $storageQuery->executeStatement();
116
+            }
117
+
118
+            if (isset($storage['share_token'])) {
119
+                $shareExternalQuery->setParameter('share_token', $storage['share_token']);
120
+                $shareExternalQuery->setParameter('remote', $storage['remote']);
121
+                $shareExternalQuery->executeStatement();
122
+            }
123
+        }
124
+
125
+        parent::tearDown();
126
+    }
127
+
128
+    private function doesStorageExist($numericId) {
129
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
130
+        $qb->select('*')
131
+            ->from('storages')
132
+            ->where($qb->expr()->eq('numeric_id', $qb->createNamedParameter($numericId)));
133
+
134
+        $qResult = $qb->executeQuery();
135
+        $result = $qResult->fetch();
136
+        $qResult->closeCursor();
137
+        if (!empty($result)) {
138
+            return true;
139
+        }
140
+
141
+        $qb = Server::get(IDBConnection::class)->getQueryBuilder();
142
+        $qb->select('*')
143
+            ->from('filecache')
144
+            ->where($qb->expr()->eq('storage', $qb->createNamedParameter($numericId)));
145
+
146
+        $qResult = $qb->executeQuery();
147
+        $result = $qResult->fetch();
148
+        $qResult->closeCursor();
149
+        if (!empty($result)) {
150
+            return true;
151
+        }
152
+
153
+        return false;
154
+    }
155
+
156
+    /**
157
+     * Test cleanup of orphaned storages
158
+     */
159
+    public function testCleanup(): void {
160
+        $input = $this->getMockBuilder(InputInterface::class)
161
+            ->disableOriginalConstructor()
162
+            ->getMock();
163
+        $output = $this->getMockBuilder(OutputInterface::class)
164
+            ->disableOriginalConstructor()
165
+            ->getMock();
166
+
167
+        // parent folder, `files`, ´test` and `welcome.txt` => 4 elements
168
+        $outputCalls = [];
169
+        $output
170
+            ->expects($this->any())
171
+            ->method('writeln')
172
+            ->willReturnCallback(function (string $text) use (&$outputCalls): void {
173
+                $outputCalls[] = $text;
174
+            });
175
+
176
+        $this->cloudIdManager
177
+            ->expects($this->any())
178
+            ->method('getCloudId')
179
+            ->willReturnCallback(function (string $user, string $remote) {
180
+                $cloudIdMock = $this->createMock(ICloudId::class);
181
+
182
+                // The remotes are already sanitized in the original data, so
183
+                // they can be directly returned.
184
+                $cloudIdMock
185
+                    ->expects($this->any())
186
+                    ->method('getRemote')
187
+                    ->willReturn($remote);
188
+
189
+                return $cloudIdMock;
190
+            });
191
+
192
+        $this->command->execute($input, $output);
193
+
194
+        $this->assertTrue($this->doesStorageExist($this->storages[0]['numeric_id']));
195
+        $this->assertTrue($this->doesStorageExist($this->storages[1]['numeric_id']));
196
+        $this->assertFalse($this->doesStorageExist($this->storages[3]['numeric_id']));
197
+        $this->assertTrue($this->doesStorageExist($this->storages[4]['numeric_id']));
198
+        $this->assertFalse($this->doesStorageExist($this->storages[5]['numeric_id']));
199
+
200
+        $this->assertEquals([
201
+            '5 remote storage(s) need(s) to be checked',
202
+            '5 remote share(s) exist',
203
+        ], array_slice($outputCalls, 0, 2));
204
+    }
205 205
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -87,8 +87,8 @@  discard block
 block discarded – undo
87 87
 			if (isset($storage['files_count'])) {
88 88
 				for ($i = 0; $i < $storage['files_count']; $i++) {
89 89
 					$filesQuery->setParameter('storage', $storage['numeric_id']);
90
-					$filesQuery->setParameter('path', 'file' . $i);
91
-					$filesQuery->setParameter('path_hash', md5('file' . $i));
90
+					$filesQuery->setParameter('path', 'file'.$i);
91
+					$filesQuery->setParameter('path_hash', md5('file'.$i));
92 92
 					$filesQuery->executeStatement();
93 93
 				}
94 94
 			}
@@ -169,14 +169,14 @@  discard block
 block discarded – undo
169 169
 		$output
170 170
 			->expects($this->any())
171 171
 			->method('writeln')
172
-			->willReturnCallback(function (string $text) use (&$outputCalls): void {
172
+			->willReturnCallback(function(string $text) use (&$outputCalls): void {
173 173
 				$outputCalls[] = $text;
174 174
 			});
175 175
 
176 176
 		$this->cloudIdManager
177 177
 			->expects($this->any())
178 178
 			->method('getCloudId')
179
-			->willReturnCallback(function (string $user, string $remote) {
179
+			->willReturnCallback(function(string $user, string $remote) {
180 180
 				$cloudIdMock = $this->createMock(ICloudId::class);
181 181
 
182 182
 				// The remotes are already sanitized in the original data, so
Please login to merge, or discard this patch.
apps/files_sharing/tests/UpdaterTest.php 1 patch
Indentation   +311 added lines, -311 removed lines patch added patch discarded remove patch
@@ -25,321 +25,321 @@
 block discarded – undo
25 25
  * @group DB
26 26
  */
27 27
 class UpdaterTest extends TestCase {
28
-	public const TEST_FOLDER_NAME = '/folder_share_updater_test';
28
+    public const TEST_FOLDER_NAME = '/folder_share_updater_test';
29 29
 
30
-	public static function setUpBeforeClass(): void {
31
-		parent::setUpBeforeClass();
32
-		Helper::registerHooks();
33
-	}
30
+    public static function setUpBeforeClass(): void {
31
+        parent::setUpBeforeClass();
32
+        Helper::registerHooks();
33
+    }
34 34
 
35
-	protected function setUp(): void {
36
-		parent::setUp();
37
-
38
-		$this->folder = self::TEST_FOLDER_NAME;
39
-
40
-		$this->filename = '/share-updater-test.txt';
41
-
42
-		// save file with content
43
-		$this->view->file_put_contents($this->filename, $this->data);
44
-		$this->view->mkdir($this->folder);
45
-		$this->view->file_put_contents($this->folder . '/' . $this->filename, $this->data);
46
-	}
47
-
48
-	protected function tearDown(): void {
49
-		if ($this->view) {
50
-			$this->view->unlink($this->filename);
51
-			$this->view->deleteAll($this->folder);
52
-		}
53
-
54
-		parent::tearDown();
55
-	}
56
-
57
-	/**
58
-	 * test deletion of a folder which contains share mount points. Share mount
59
-	 * points should be unshared before the folder gets deleted so
60
-	 * that the mount point doesn't end up at the trash bin
61
-	 */
62
-	public function testDeleteParentFolder(): void {
63
-		$appManager = Server::get(IAppManager::class);
64
-		$status = $appManager->isEnabledForUser('files_trashbin');
65
-		$appManager->enableApp('files_trashbin');
66
-
67
-		// register trashbin hooks
68
-		$trashbinApp = new Application();
69
-		$trashbinApp->boot($this->createMock(IBootContext::class));
35
+    protected function setUp(): void {
36
+        parent::setUp();
37
+
38
+        $this->folder = self::TEST_FOLDER_NAME;
39
+
40
+        $this->filename = '/share-updater-test.txt';
41
+
42
+        // save file with content
43
+        $this->view->file_put_contents($this->filename, $this->data);
44
+        $this->view->mkdir($this->folder);
45
+        $this->view->file_put_contents($this->folder . '/' . $this->filename, $this->data);
46
+    }
47
+
48
+    protected function tearDown(): void {
49
+        if ($this->view) {
50
+            $this->view->unlink($this->filename);
51
+            $this->view->deleteAll($this->folder);
52
+        }
53
+
54
+        parent::tearDown();
55
+    }
56
+
57
+    /**
58
+     * test deletion of a folder which contains share mount points. Share mount
59
+     * points should be unshared before the folder gets deleted so
60
+     * that the mount point doesn't end up at the trash bin
61
+     */
62
+    public function testDeleteParentFolder(): void {
63
+        $appManager = Server::get(IAppManager::class);
64
+        $status = $appManager->isEnabledForUser('files_trashbin');
65
+        $appManager->enableApp('files_trashbin');
66
+
67
+        // register trashbin hooks
68
+        $trashbinApp = new Application();
69
+        $trashbinApp->boot($this->createMock(IBootContext::class));
70 70
 
71
-		$fileinfo = Filesystem::getFileInfo($this->folder);
72
-		$this->assertTrue($fileinfo instanceof FileInfo);
71
+        $fileinfo = Filesystem::getFileInfo($this->folder);
72
+        $this->assertTrue($fileinfo instanceof FileInfo);
73 73
 
74
-		$this->share(
75
-			IShare::TYPE_USER,
76
-			$this->folder,
77
-			self::TEST_FILES_SHARING_API_USER1,
78
-			self::TEST_FILES_SHARING_API_USER2,
79
-			Constants::PERMISSION_ALL
80
-		);
81
-
82
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
83
-		$view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
84
-
85
-		// check if user2 can see the shared folder
86
-		$this->assertTrue($view->file_exists($this->folder));
87
-
88
-		$foldersShared = $this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER1, IShare::TYPE_USER);
89
-		$this->assertCount(1, $foldersShared);
90
-
91
-		$view->mkdir('localFolder');
92
-		$view->file_put_contents('localFolder/localFile.txt', 'local file');
93
-
94
-		$view->rename($this->folder, 'localFolder/' . $this->folder);
95
-
96
-		// share mount point should now be moved to the subfolder
97
-		$this->assertFalse($view->file_exists($this->folder));
98
-		$this->assertTrue($view->file_exists('localFolder/' . $this->folder));
99
-
100
-		$view->unlink('localFolder');
101
-
102
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
103
-
104
-		// shared folder should be unshared
105
-		$foldersShared = $this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER1, IShare::TYPE_USER);
106
-		$this->assertCount(0, $foldersShared);
107
-
108
-		// trashbin should contain the local file but not the mount point
109
-		$rootView = new View('/' . self::TEST_FILES_SHARING_API_USER2);
110
-		$trashContent = \OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_FILES_SHARING_API_USER2);
111
-		$this->assertSame(1, count($trashContent));
112
-		$firstElement = reset($trashContent);
113
-		$timestamp = $firstElement['mtime'];
114
-		$this->assertTrue($rootView->file_exists('files_trashbin/files/localFolder.d' . $timestamp . '/localFile.txt'));
115
-		$this->assertFalse($rootView->file_exists('files_trashbin/files/localFolder.d' . $timestamp . '/' . $this->folder));
116
-
117
-		//cleanup
118
-		$rootView->deleteAll('files_trashin');
119
-
120
-		if ($status === false) {
121
-			$appManager->disableApp('files_trashbin');
122
-		}
74
+        $this->share(
75
+            IShare::TYPE_USER,
76
+            $this->folder,
77
+            self::TEST_FILES_SHARING_API_USER1,
78
+            self::TEST_FILES_SHARING_API_USER2,
79
+            Constants::PERMISSION_ALL
80
+        );
81
+
82
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
83
+        $view = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
84
+
85
+        // check if user2 can see the shared folder
86
+        $this->assertTrue($view->file_exists($this->folder));
87
+
88
+        $foldersShared = $this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER1, IShare::TYPE_USER);
89
+        $this->assertCount(1, $foldersShared);
90
+
91
+        $view->mkdir('localFolder');
92
+        $view->file_put_contents('localFolder/localFile.txt', 'local file');
93
+
94
+        $view->rename($this->folder, 'localFolder/' . $this->folder);
95
+
96
+        // share mount point should now be moved to the subfolder
97
+        $this->assertFalse($view->file_exists($this->folder));
98
+        $this->assertTrue($view->file_exists('localFolder/' . $this->folder));
99
+
100
+        $view->unlink('localFolder');
101
+
102
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
103
+
104
+        // shared folder should be unshared
105
+        $foldersShared = $this->shareManager->getSharesBy(self::TEST_FILES_SHARING_API_USER1, IShare::TYPE_USER);
106
+        $this->assertCount(0, $foldersShared);
107
+
108
+        // trashbin should contain the local file but not the mount point
109
+        $rootView = new View('/' . self::TEST_FILES_SHARING_API_USER2);
110
+        $trashContent = \OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_FILES_SHARING_API_USER2);
111
+        $this->assertSame(1, count($trashContent));
112
+        $firstElement = reset($trashContent);
113
+        $timestamp = $firstElement['mtime'];
114
+        $this->assertTrue($rootView->file_exists('files_trashbin/files/localFolder.d' . $timestamp . '/localFile.txt'));
115
+        $this->assertFalse($rootView->file_exists('files_trashbin/files/localFolder.d' . $timestamp . '/' . $this->folder));
116
+
117
+        //cleanup
118
+        $rootView->deleteAll('files_trashin');
119
+
120
+        if ($status === false) {
121
+            $appManager->disableApp('files_trashbin');
122
+        }
123 123
 
124
-		Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
125
-	}
124
+        Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
125
+    }
126 126
 
127
-	public static function shareFolderProvider() {
128
-		return [
129
-			['/'],
130
-			['/my_shares'],
131
-		];
132
-	}
133
-
134
-	/**
135
-	 * if a file gets shared the etag for the recipients root should change
136
-	 *
137
-	 *
138
-	 * @param string $shareFolder share folder to use
139
-	 */
140
-	#[\PHPUnit\Framework\Attributes\DataProvider('shareFolderProvider')]
141
-	public function testShareFile($shareFolder): void {
142
-		$config = Server::get(IConfig::class);
143
-		$oldShareFolder = $config->getSystemValue('share_folder');
144
-		$config->setSystemValue('share_folder', $shareFolder);
145
-
146
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
147
-
148
-		$beforeShareRoot = Filesystem::getFileInfo('');
149
-		$etagBeforeShareRoot = $beforeShareRoot->getEtag();
150
-
151
-		Filesystem::mkdir($shareFolder);
152
-
153
-		$beforeShareDir = Filesystem::getFileInfo($shareFolder);
154
-		$etagBeforeShareDir = $beforeShareDir->getEtag();
155
-
156
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
157
-
158
-		$share = $this->share(
159
-			IShare::TYPE_USER,
160
-			$this->folder,
161
-			self::TEST_FILES_SHARING_API_USER1,
162
-			self::TEST_FILES_SHARING_API_USER2,
163
-			Constants::PERMISSION_ALL
164
-		);
165
-
166
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
167
-
168
-		$afterShareRoot = Filesystem::getFileInfo('');
169
-		$etagAfterShareRoot = $afterShareRoot->getEtag();
170
-
171
-		$afterShareDir = Filesystem::getFileInfo($shareFolder);
172
-		$etagAfterShareDir = $afterShareDir->getEtag();
173
-
174
-		$this->assertTrue(is_string($etagBeforeShareRoot));
175
-		$this->assertTrue(is_string($etagBeforeShareDir));
176
-		$this->assertTrue(is_string($etagAfterShareRoot));
177
-		$this->assertTrue(is_string($etagAfterShareDir));
178
-		$this->assertTrue($etagBeforeShareRoot !== $etagAfterShareRoot);
179
-		$this->assertTrue($etagBeforeShareDir !== $etagAfterShareDir);
180
-
181
-		// cleanup
182
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
183
-		$this->shareManager->deleteShare($share);
184
-
185
-		$config->setSystemValue('share_folder', $oldShareFolder);
186
-	}
187
-
188
-	/**
189
-	 * if a folder gets renamed all children mount points should be renamed too
190
-	 */
191
-	public function testRename(): void {
192
-		$fileinfo = Filesystem::getFileInfo($this->folder);
193
-
194
-		$share = $this->share(
195
-			IShare::TYPE_USER,
196
-			$this->folder,
197
-			self::TEST_FILES_SHARING_API_USER1,
198
-			self::TEST_FILES_SHARING_API_USER2,
199
-			Constants::PERMISSION_ALL
200
-		);
201
-
202
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
203
-
204
-		// make sure that the shared folder exists
205
-		$this->assertTrue(Filesystem::file_exists($this->folder));
206
-
207
-		Filesystem::mkdir('oldTarget');
208
-		Filesystem::mkdir('oldTarget/subfolder');
209
-		Filesystem::mkdir('newTarget');
210
-
211
-		Filesystem::rename($this->folder, 'oldTarget/subfolder/' . $this->folder);
212
-
213
-		// re-login to make sure that the new mount points are initialized
214
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
215
-
216
-		Filesystem::rename('/oldTarget', '/newTarget/oldTarget');
217
-
218
-		// re-login to make sure that the new mount points are initialized
219
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
220
-
221
-		$this->assertTrue(Filesystem::file_exists('/newTarget/oldTarget/subfolder/' . $this->folder));
222
-
223
-		// cleanup
224
-		$this->shareManager->deleteShare($share);
225
-	}
226
-
227
-	/**
228
-	 * If a folder gets moved into shared folder, children shares should have their uid_owner and permissions adjusted
229
-	 * user1
230
-	 * 	|-folder1 --> shared with user2
231
-	 * user2
232
-	 * 	|-folder2 --> shared with user3 and moved into folder1
233
-	 * 	  |-subfolder1 --> shared with user3
234
-	 * 	  |-file1.txt --> shared with user3
235
-	 * 	  |-subfolder2
236
-	 * 	    |-file2.txt --> shared with user3
237
-	 */
238
-	public function testMovedIntoShareChangeOwner(): void {
239
-		$this->markTestSkipped('Skipped because this is failing with S3 as primary as file id are change when moved.');
240
-
241
-		// user1 creates folder1
242
-		$viewUser1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
243
-		$folder1 = 'folder1';
244
-		$viewUser1->mkdir($folder1);
245
-
246
-		// user1 shares folder1 to user2
247
-		$folder1Share = $this->share(
248
-			IShare::TYPE_USER,
249
-			$folder1,
250
-			self::TEST_FILES_SHARING_API_USER1,
251
-			self::TEST_FILES_SHARING_API_USER2,
252
-			Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
253
-		);
254
-
255
-		$this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
256
-		$viewUser2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
257
-		// Create user2 files
258
-		$folder2 = 'folder2';
259
-		$viewUser2->mkdir($folder2);
260
-		$file1 = 'folder2/file1.txt';
261
-		$viewUser2->touch($file1);
262
-		$subfolder1 = 'folder2/subfolder1';
263
-		$viewUser2->mkdir($subfolder1);
264
-		$subfolder2 = 'folder2/subfolder2';
265
-		$viewUser2->mkdir($subfolder2);
266
-		$file2 = 'folder2/subfolder2/file2.txt';
267
-		$viewUser2->touch($file2);
268
-
269
-		// user2 shares folder2 to user3
270
-		$folder2Share = $this->share(
271
-			IShare::TYPE_USER,
272
-			$folder2,
273
-			self::TEST_FILES_SHARING_API_USER2,
274
-			self::TEST_FILES_SHARING_API_USER3,
275
-			Constants::PERMISSION_ALL
276
-		);
277
-		// user2 shares folder2/file1 to user3
278
-		$file1Share = $this->share(
279
-			IShare::TYPE_USER,
280
-			$file1,
281
-			self::TEST_FILES_SHARING_API_USER2,
282
-			self::TEST_FILES_SHARING_API_USER3,
283
-			Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
284
-		);
285
-		// user2 shares subfolder1 to user3
286
-		$subfolder1Share = $this->share(
287
-			IShare::TYPE_USER,
288
-			$subfolder1,
289
-			self::TEST_FILES_SHARING_API_USER2,
290
-			self::TEST_FILES_SHARING_API_USER3,
291
-			Constants::PERMISSION_ALL
292
-		);
293
-		// user2 shares subfolder2/file2.txt to user3
294
-		$file2Share = $this->share(
295
-			IShare::TYPE_USER,
296
-			$file2,
297
-			self::TEST_FILES_SHARING_API_USER2,
298
-			self::TEST_FILES_SHARING_API_USER3,
299
-			Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
300
-		);
301
-
302
-		// user2 moves folder2 into folder1
303
-		$viewUser2->rename($folder2, $folder1 . '/' . $folder2);
304
-		$folder2Share = $this->shareManager->getShareById($folder2Share->getFullId());
305
-		$file1Share = $this->shareManager->getShareById($file1Share->getFullId());
306
-		$subfolder1Share = $this->shareManager->getShareById($subfolder1Share->getFullId());
307
-		$file2Share = $this->shareManager->getShareById($file2Share->getFullId());
308
-
309
-		// Expect uid_owner of both shares to be user1
310
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $folder2Share->getShareOwner());
311
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $file1Share->getShareOwner());
312
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $subfolder1Share->getShareOwner());
313
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $file2Share->getShareOwner());
314
-		// Expect permissions to be limited by the permissions of the destination share
315
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $folder2Share->getPermissions());
316
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file1Share->getPermissions());
317
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $subfolder1Share->getPermissions());
318
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file2Share->getPermissions());
319
-
320
-		// user2 moves folder2 out of folder1
321
-		$viewUser2->rename($folder1 . '/' . $folder2, $folder2);
322
-		$folder2Share = $this->shareManager->getShareById($folder2Share->getFullId());
323
-		$file1Share = $this->shareManager->getShareById($file1Share->getFullId());
324
-		$subfolder1Share = $this->shareManager->getShareById($subfolder1Share->getFullId());
325
-		$file2Share = $this->shareManager->getShareById($file2Share->getFullId());
326
-
327
-		// Expect uid_owner of both shares to be user2
328
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $folder2Share->getShareOwner());
329
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $file1Share->getShareOwner());
330
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $subfolder1Share->getShareOwner());
331
-		$this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $file2Share->getShareOwner());
332
-		// Expect permissions to not change
333
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $folder2Share->getPermissions());
334
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file1Share->getPermissions());
335
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $subfolder1Share->getPermissions());
336
-		$this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file2Share->getPermissions());
337
-
338
-		// cleanup
339
-		$this->shareManager->deleteShare($folder1Share);
340
-		$this->shareManager->deleteShare($folder2Share);
341
-		$this->shareManager->deleteShare($file1Share);
342
-		$this->shareManager->deleteShare($subfolder1Share);
343
-		$this->shareManager->deleteShare($file2Share);
344
-	}
127
+    public static function shareFolderProvider() {
128
+        return [
129
+            ['/'],
130
+            ['/my_shares'],
131
+        ];
132
+    }
133
+
134
+    /**
135
+     * if a file gets shared the etag for the recipients root should change
136
+     *
137
+     *
138
+     * @param string $shareFolder share folder to use
139
+     */
140
+    #[\PHPUnit\Framework\Attributes\DataProvider('shareFolderProvider')]
141
+    public function testShareFile($shareFolder): void {
142
+        $config = Server::get(IConfig::class);
143
+        $oldShareFolder = $config->getSystemValue('share_folder');
144
+        $config->setSystemValue('share_folder', $shareFolder);
145
+
146
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
147
+
148
+        $beforeShareRoot = Filesystem::getFileInfo('');
149
+        $etagBeforeShareRoot = $beforeShareRoot->getEtag();
150
+
151
+        Filesystem::mkdir($shareFolder);
152
+
153
+        $beforeShareDir = Filesystem::getFileInfo($shareFolder);
154
+        $etagBeforeShareDir = $beforeShareDir->getEtag();
155
+
156
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
157
+
158
+        $share = $this->share(
159
+            IShare::TYPE_USER,
160
+            $this->folder,
161
+            self::TEST_FILES_SHARING_API_USER1,
162
+            self::TEST_FILES_SHARING_API_USER2,
163
+            Constants::PERMISSION_ALL
164
+        );
165
+
166
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
167
+
168
+        $afterShareRoot = Filesystem::getFileInfo('');
169
+        $etagAfterShareRoot = $afterShareRoot->getEtag();
170
+
171
+        $afterShareDir = Filesystem::getFileInfo($shareFolder);
172
+        $etagAfterShareDir = $afterShareDir->getEtag();
173
+
174
+        $this->assertTrue(is_string($etagBeforeShareRoot));
175
+        $this->assertTrue(is_string($etagBeforeShareDir));
176
+        $this->assertTrue(is_string($etagAfterShareRoot));
177
+        $this->assertTrue(is_string($etagAfterShareDir));
178
+        $this->assertTrue($etagBeforeShareRoot !== $etagAfterShareRoot);
179
+        $this->assertTrue($etagBeforeShareDir !== $etagAfterShareDir);
180
+
181
+        // cleanup
182
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER1);
183
+        $this->shareManager->deleteShare($share);
184
+
185
+        $config->setSystemValue('share_folder', $oldShareFolder);
186
+    }
187
+
188
+    /**
189
+     * if a folder gets renamed all children mount points should be renamed too
190
+     */
191
+    public function testRename(): void {
192
+        $fileinfo = Filesystem::getFileInfo($this->folder);
193
+
194
+        $share = $this->share(
195
+            IShare::TYPE_USER,
196
+            $this->folder,
197
+            self::TEST_FILES_SHARING_API_USER1,
198
+            self::TEST_FILES_SHARING_API_USER2,
199
+            Constants::PERMISSION_ALL
200
+        );
201
+
202
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
203
+
204
+        // make sure that the shared folder exists
205
+        $this->assertTrue(Filesystem::file_exists($this->folder));
206
+
207
+        Filesystem::mkdir('oldTarget');
208
+        Filesystem::mkdir('oldTarget/subfolder');
209
+        Filesystem::mkdir('newTarget');
210
+
211
+        Filesystem::rename($this->folder, 'oldTarget/subfolder/' . $this->folder);
212
+
213
+        // re-login to make sure that the new mount points are initialized
214
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
215
+
216
+        Filesystem::rename('/oldTarget', '/newTarget/oldTarget');
217
+
218
+        // re-login to make sure that the new mount points are initialized
219
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
220
+
221
+        $this->assertTrue(Filesystem::file_exists('/newTarget/oldTarget/subfolder/' . $this->folder));
222
+
223
+        // cleanup
224
+        $this->shareManager->deleteShare($share);
225
+    }
226
+
227
+    /**
228
+     * If a folder gets moved into shared folder, children shares should have their uid_owner and permissions adjusted
229
+     * user1
230
+     * 	|-folder1 --> shared with user2
231
+     * user2
232
+     * 	|-folder2 --> shared with user3 and moved into folder1
233
+     * 	  |-subfolder1 --> shared with user3
234
+     * 	  |-file1.txt --> shared with user3
235
+     * 	  |-subfolder2
236
+     * 	    |-file2.txt --> shared with user3
237
+     */
238
+    public function testMovedIntoShareChangeOwner(): void {
239
+        $this->markTestSkipped('Skipped because this is failing with S3 as primary as file id are change when moved.');
240
+
241
+        // user1 creates folder1
242
+        $viewUser1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
243
+        $folder1 = 'folder1';
244
+        $viewUser1->mkdir($folder1);
245
+
246
+        // user1 shares folder1 to user2
247
+        $folder1Share = $this->share(
248
+            IShare::TYPE_USER,
249
+            $folder1,
250
+            self::TEST_FILES_SHARING_API_USER1,
251
+            self::TEST_FILES_SHARING_API_USER2,
252
+            Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
253
+        );
254
+
255
+        $this->loginHelper(self::TEST_FILES_SHARING_API_USER2);
256
+        $viewUser2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
257
+        // Create user2 files
258
+        $folder2 = 'folder2';
259
+        $viewUser2->mkdir($folder2);
260
+        $file1 = 'folder2/file1.txt';
261
+        $viewUser2->touch($file1);
262
+        $subfolder1 = 'folder2/subfolder1';
263
+        $viewUser2->mkdir($subfolder1);
264
+        $subfolder2 = 'folder2/subfolder2';
265
+        $viewUser2->mkdir($subfolder2);
266
+        $file2 = 'folder2/subfolder2/file2.txt';
267
+        $viewUser2->touch($file2);
268
+
269
+        // user2 shares folder2 to user3
270
+        $folder2Share = $this->share(
271
+            IShare::TYPE_USER,
272
+            $folder2,
273
+            self::TEST_FILES_SHARING_API_USER2,
274
+            self::TEST_FILES_SHARING_API_USER3,
275
+            Constants::PERMISSION_ALL
276
+        );
277
+        // user2 shares folder2/file1 to user3
278
+        $file1Share = $this->share(
279
+            IShare::TYPE_USER,
280
+            $file1,
281
+            self::TEST_FILES_SHARING_API_USER2,
282
+            self::TEST_FILES_SHARING_API_USER3,
283
+            Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
284
+        );
285
+        // user2 shares subfolder1 to user3
286
+        $subfolder1Share = $this->share(
287
+            IShare::TYPE_USER,
288
+            $subfolder1,
289
+            self::TEST_FILES_SHARING_API_USER2,
290
+            self::TEST_FILES_SHARING_API_USER3,
291
+            Constants::PERMISSION_ALL
292
+        );
293
+        // user2 shares subfolder2/file2.txt to user3
294
+        $file2Share = $this->share(
295
+            IShare::TYPE_USER,
296
+            $file2,
297
+            self::TEST_FILES_SHARING_API_USER2,
298
+            self::TEST_FILES_SHARING_API_USER3,
299
+            Constants::PERMISSION_READ | Constants::PERMISSION_SHARE
300
+        );
301
+
302
+        // user2 moves folder2 into folder1
303
+        $viewUser2->rename($folder2, $folder1 . '/' . $folder2);
304
+        $folder2Share = $this->shareManager->getShareById($folder2Share->getFullId());
305
+        $file1Share = $this->shareManager->getShareById($file1Share->getFullId());
306
+        $subfolder1Share = $this->shareManager->getShareById($subfolder1Share->getFullId());
307
+        $file2Share = $this->shareManager->getShareById($file2Share->getFullId());
308
+
309
+        // Expect uid_owner of both shares to be user1
310
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $folder2Share->getShareOwner());
311
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $file1Share->getShareOwner());
312
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $subfolder1Share->getShareOwner());
313
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $file2Share->getShareOwner());
314
+        // Expect permissions to be limited by the permissions of the destination share
315
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $folder2Share->getPermissions());
316
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file1Share->getPermissions());
317
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $subfolder1Share->getPermissions());
318
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file2Share->getPermissions());
319
+
320
+        // user2 moves folder2 out of folder1
321
+        $viewUser2->rename($folder1 . '/' . $folder2, $folder2);
322
+        $folder2Share = $this->shareManager->getShareById($folder2Share->getFullId());
323
+        $file1Share = $this->shareManager->getShareById($file1Share->getFullId());
324
+        $subfolder1Share = $this->shareManager->getShareById($subfolder1Share->getFullId());
325
+        $file2Share = $this->shareManager->getShareById($file2Share->getFullId());
326
+
327
+        // Expect uid_owner of both shares to be user2
328
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $folder2Share->getShareOwner());
329
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $file1Share->getShareOwner());
330
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $subfolder1Share->getShareOwner());
331
+        $this->assertEquals(self::TEST_FILES_SHARING_API_USER2, $file2Share->getShareOwner());
332
+        // Expect permissions to not change
333
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $folder2Share->getPermissions());
334
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file1Share->getPermissions());
335
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $subfolder1Share->getPermissions());
336
+        $this->assertEquals(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE, $file2Share->getPermissions());
337
+
338
+        // cleanup
339
+        $this->shareManager->deleteShare($folder1Share);
340
+        $this->shareManager->deleteShare($folder2Share);
341
+        $this->shareManager->deleteShare($file1Share);
342
+        $this->shareManager->deleteShare($subfolder1Share);
343
+        $this->shareManager->deleteShare($file2Share);
344
+    }
345 345
 }
Please login to merge, or discard this patch.