Passed
Push — master ( 9cae15...9d3c8b )
by Robin
41:44 queued 25:43
created
lib/private/Share20/Manager.php 1 patch
Indentation   +1988 added lines, -1988 removed lines patch added patch discarded remove patch
@@ -82,2013 +82,2013 @@
 block discarded – undo
82 82
  * This class is the communication hub for all sharing related operations.
83 83
  */
84 84
 class Manager implements IManager {
85
-	/** @var IProviderFactory */
86
-	private $factory;
87
-	private LoggerInterface $logger;
88
-	/** @var IConfig */
89
-	private $config;
90
-	/** @var ISecureRandom */
91
-	private $secureRandom;
92
-	/** @var IHasher */
93
-	private $hasher;
94
-	/** @var IMountManager */
95
-	private $mountManager;
96
-	/** @var IGroupManager */
97
-	private $groupManager;
98
-	/** @var IL10N */
99
-	private $l;
100
-	/** @var IFactory */
101
-	private $l10nFactory;
102
-	/** @var IUserManager */
103
-	private $userManager;
104
-	/** @var IRootFolder */
105
-	private $rootFolder;
106
-	/** @var CappedMemoryCache */
107
-	private $sharingDisabledForUsersCache;
108
-	/** @var EventDispatcherInterface */
109
-	private $legacyDispatcher;
110
-	/** @var LegacyHooks */
111
-	private $legacyHooks;
112
-	/** @var IMailer */
113
-	private $mailer;
114
-	/** @var IURLGenerator */
115
-	private $urlGenerator;
116
-	/** @var \OC_Defaults */
117
-	private $defaults;
118
-	/** @var IEventDispatcher */
119
-	private $dispatcher;
120
-	/** @var IUserSession */
121
-	private $userSession;
122
-	/** @var KnownUserService */
123
-	private $knownUserService;
124
-
125
-	public function __construct(
126
-		LoggerInterface $logger,
127
-		IConfig $config,
128
-		ISecureRandom $secureRandom,
129
-		IHasher $hasher,
130
-		IMountManager $mountManager,
131
-		IGroupManager $groupManager,
132
-		IL10N $l,
133
-		IFactory $l10nFactory,
134
-		IProviderFactory $factory,
135
-		IUserManager $userManager,
136
-		IRootFolder $rootFolder,
137
-		EventDispatcherInterface $legacyDispatcher,
138
-		IMailer $mailer,
139
-		IURLGenerator $urlGenerator,
140
-		\OC_Defaults $defaults,
141
-		IEventDispatcher $dispatcher,
142
-		IUserSession $userSession,
143
-		KnownUserService $knownUserService
144
-	) {
145
-		$this->logger = $logger;
146
-		$this->config = $config;
147
-		$this->secureRandom = $secureRandom;
148
-		$this->hasher = $hasher;
149
-		$this->mountManager = $mountManager;
150
-		$this->groupManager = $groupManager;
151
-		$this->l = $l;
152
-		$this->l10nFactory = $l10nFactory;
153
-		$this->factory = $factory;
154
-		$this->userManager = $userManager;
155
-		$this->rootFolder = $rootFolder;
156
-		$this->legacyDispatcher = $legacyDispatcher;
157
-		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
158
-		// The constructor of LegacyHooks registers the listeners of share events
159
-		// do not remove if those are not properly migrated
160
-		$this->legacyHooks = new LegacyHooks($this->legacyDispatcher);
161
-		$this->mailer = $mailer;
162
-		$this->urlGenerator = $urlGenerator;
163
-		$this->defaults = $defaults;
164
-		$this->dispatcher = $dispatcher;
165
-		$this->userSession = $userSession;
166
-		$this->knownUserService = $knownUserService;
167
-	}
168
-
169
-	/**
170
-	 * Convert from a full share id to a tuple (providerId, shareId)
171
-	 *
172
-	 * @param string $id
173
-	 * @return string[]
174
-	 */
175
-	private function splitFullId($id) {
176
-		return explode(':', $id, 2);
177
-	}
178
-
179
-	/**
180
-	 * Verify if a password meets all requirements
181
-	 *
182
-	 * @param string $password
183
-	 * @throws \Exception
184
-	 */
185
-	protected function verifyPassword($password) {
186
-		if ($password === null) {
187
-			// No password is set, check if this is allowed.
188
-			if ($this->shareApiLinkEnforcePassword()) {
189
-				throw new \InvalidArgumentException('Passwords are enforced for link and mail shares');
190
-			}
191
-
192
-			return;
193
-		}
194
-
195
-		// Let others verify the password
196
-		try {
197
-			$this->legacyDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
198
-		} catch (HintException $e) {
199
-			throw new \Exception($e->getHint());
200
-		}
201
-	}
202
-
203
-	/**
204
-	 * Check for generic requirements before creating a share
205
-	 *
206
-	 * @param IShare $share
207
-	 * @throws \InvalidArgumentException
208
-	 * @throws GenericShareException
209
-	 *
210
-	 * @suppress PhanUndeclaredClassMethod
211
-	 */
212
-	protected function generalCreateChecks(IShare $share) {
213
-		if ($share->getShareType() === IShare::TYPE_USER) {
214
-			// We expect a valid user as sharedWith for user shares
215
-			if (!$this->userManager->userExists($share->getSharedWith())) {
216
-				throw new \InvalidArgumentException('SharedWith is not a valid user');
217
-			}
218
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
219
-			// We expect a valid group as sharedWith for group shares
220
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
221
-				throw new \InvalidArgumentException('SharedWith is not a valid group');
222
-			}
223
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
224
-			// No check for TYPE_EMAIL here as we have a recipient for them
225
-			if ($share->getSharedWith() !== null) {
226
-				throw new \InvalidArgumentException('SharedWith should be empty');
227
-			}
228
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
229
-			if ($share->getSharedWith() === null) {
230
-				throw new \InvalidArgumentException('SharedWith should not be empty');
231
-			}
232
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
233
-			if ($share->getSharedWith() === null) {
234
-				throw new \InvalidArgumentException('SharedWith should not be empty');
235
-			}
236
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
237
-			if ($share->getSharedWith() === null) {
238
-				throw new \InvalidArgumentException('SharedWith should not be empty');
239
-			}
240
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
241
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
242
-			if ($circle === null) {
243
-				throw new \InvalidArgumentException('SharedWith is not a valid circle');
244
-			}
245
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
246
-		} elseif ($share->getShareType() === IShare::TYPE_DECK) {
247
-		} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
248
-		} else {
249
-			// We cannot handle other types yet
250
-			throw new \InvalidArgumentException('unknown share type');
251
-		}
252
-
253
-		// Verify the initiator of the share is set
254
-		if ($share->getSharedBy() === null) {
255
-			throw new \InvalidArgumentException('SharedBy should be set');
256
-		}
257
-
258
-		// Cannot share with yourself
259
-		if ($share->getShareType() === IShare::TYPE_USER &&
260
-			$share->getSharedWith() === $share->getSharedBy()) {
261
-			throw new \InvalidArgumentException('Cannot share with yourself');
262
-		}
263
-
264
-		// The path should be set
265
-		if ($share->getNode() === null) {
266
-			throw new \InvalidArgumentException('Path should be set');
267
-		}
268
-
269
-		// And it should be a file or a folder
270
-		if (!($share->getNode() instanceof \OCP\Files\File) &&
271
-			!($share->getNode() instanceof \OCP\Files\Folder)) {
272
-			throw new \InvalidArgumentException('Path should be either a file or a folder');
273
-		}
274
-
275
-		// And you cannot share your rootfolder
276
-		if ($this->userManager->userExists($share->getSharedBy())) {
277
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
278
-		} else {
279
-			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
280
-		}
281
-		if ($userFolder->getId() === $share->getNode()->getId()) {
282
-			throw new \InvalidArgumentException('You cannot share your root folder');
283
-		}
284
-
285
-		// Check if we actually have share permissions
286
-		if (!$share->getNode()->isShareable()) {
287
-			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]);
288
-			throw new GenericShareException($message_t, $message_t, 404);
289
-		}
290
-
291
-		// Permissions should be set
292
-		if ($share->getPermissions() === null) {
293
-			throw new \InvalidArgumentException('A share requires permissions');
294
-		}
295
-
296
-		$isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
297
-		$permissions = 0;
298
-
299
-		if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
300
-			$userMounts = array_filter($userFolder->getById($share->getNode()->getId()), function ($mount) {
301
-				// We need to filter since there might be other mountpoints that contain the file
302
-				// e.g. if the user has access to the same external storage that the file is originating from
303
-				return $mount->getStorage()->instanceOfStorage(ISharedStorage::class);
304
-			});
305
-			$userMount = array_shift($userMounts);
306
-			if ($userMount === null) {
307
-				throw new GenericShareException('Could not get proper share mount for ' . $share->getNode()->getId() . '. Failing since else the next calls are called with null');
308
-			}
309
-			$mount = $userMount->getMountPoint();
310
-			// When it's a reshare use the parent share permissions as maximum
311
-			$userMountPointId = $mount->getStorageRootId();
312
-			$userMountPoints = $userFolder->getById($userMountPointId);
313
-			$userMountPoint = array_shift($userMountPoints);
314
-
315
-			if ($userMountPoint === null) {
316
-				throw new GenericShareException('Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null');
317
-			}
318
-
319
-			/* Check if this is an incoming share */
320
-			$incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0);
321
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0));
322
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0));
323
-			$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0));
324
-
325
-			/** @var IShare[] $incomingShares */
326
-			if (!empty($incomingShares)) {
327
-				foreach ($incomingShares as $incomingShare) {
328
-					$permissions |= $incomingShare->getPermissions();
329
-				}
330
-			}
331
-		} else {
332
-			/*
85
+    /** @var IProviderFactory */
86
+    private $factory;
87
+    private LoggerInterface $logger;
88
+    /** @var IConfig */
89
+    private $config;
90
+    /** @var ISecureRandom */
91
+    private $secureRandom;
92
+    /** @var IHasher */
93
+    private $hasher;
94
+    /** @var IMountManager */
95
+    private $mountManager;
96
+    /** @var IGroupManager */
97
+    private $groupManager;
98
+    /** @var IL10N */
99
+    private $l;
100
+    /** @var IFactory */
101
+    private $l10nFactory;
102
+    /** @var IUserManager */
103
+    private $userManager;
104
+    /** @var IRootFolder */
105
+    private $rootFolder;
106
+    /** @var CappedMemoryCache */
107
+    private $sharingDisabledForUsersCache;
108
+    /** @var EventDispatcherInterface */
109
+    private $legacyDispatcher;
110
+    /** @var LegacyHooks */
111
+    private $legacyHooks;
112
+    /** @var IMailer */
113
+    private $mailer;
114
+    /** @var IURLGenerator */
115
+    private $urlGenerator;
116
+    /** @var \OC_Defaults */
117
+    private $defaults;
118
+    /** @var IEventDispatcher */
119
+    private $dispatcher;
120
+    /** @var IUserSession */
121
+    private $userSession;
122
+    /** @var KnownUserService */
123
+    private $knownUserService;
124
+
125
+    public function __construct(
126
+        LoggerInterface $logger,
127
+        IConfig $config,
128
+        ISecureRandom $secureRandom,
129
+        IHasher $hasher,
130
+        IMountManager $mountManager,
131
+        IGroupManager $groupManager,
132
+        IL10N $l,
133
+        IFactory $l10nFactory,
134
+        IProviderFactory $factory,
135
+        IUserManager $userManager,
136
+        IRootFolder $rootFolder,
137
+        EventDispatcherInterface $legacyDispatcher,
138
+        IMailer $mailer,
139
+        IURLGenerator $urlGenerator,
140
+        \OC_Defaults $defaults,
141
+        IEventDispatcher $dispatcher,
142
+        IUserSession $userSession,
143
+        KnownUserService $knownUserService
144
+    ) {
145
+        $this->logger = $logger;
146
+        $this->config = $config;
147
+        $this->secureRandom = $secureRandom;
148
+        $this->hasher = $hasher;
149
+        $this->mountManager = $mountManager;
150
+        $this->groupManager = $groupManager;
151
+        $this->l = $l;
152
+        $this->l10nFactory = $l10nFactory;
153
+        $this->factory = $factory;
154
+        $this->userManager = $userManager;
155
+        $this->rootFolder = $rootFolder;
156
+        $this->legacyDispatcher = $legacyDispatcher;
157
+        $this->sharingDisabledForUsersCache = new CappedMemoryCache();
158
+        // The constructor of LegacyHooks registers the listeners of share events
159
+        // do not remove if those are not properly migrated
160
+        $this->legacyHooks = new LegacyHooks($this->legacyDispatcher);
161
+        $this->mailer = $mailer;
162
+        $this->urlGenerator = $urlGenerator;
163
+        $this->defaults = $defaults;
164
+        $this->dispatcher = $dispatcher;
165
+        $this->userSession = $userSession;
166
+        $this->knownUserService = $knownUserService;
167
+    }
168
+
169
+    /**
170
+     * Convert from a full share id to a tuple (providerId, shareId)
171
+     *
172
+     * @param string $id
173
+     * @return string[]
174
+     */
175
+    private function splitFullId($id) {
176
+        return explode(':', $id, 2);
177
+    }
178
+
179
+    /**
180
+     * Verify if a password meets all requirements
181
+     *
182
+     * @param string $password
183
+     * @throws \Exception
184
+     */
185
+    protected function verifyPassword($password) {
186
+        if ($password === null) {
187
+            // No password is set, check if this is allowed.
188
+            if ($this->shareApiLinkEnforcePassword()) {
189
+                throw new \InvalidArgumentException('Passwords are enforced for link and mail shares');
190
+            }
191
+
192
+            return;
193
+        }
194
+
195
+        // Let others verify the password
196
+        try {
197
+            $this->legacyDispatcher->dispatch(new ValidatePasswordPolicyEvent($password));
198
+        } catch (HintException $e) {
199
+            throw new \Exception($e->getHint());
200
+        }
201
+    }
202
+
203
+    /**
204
+     * Check for generic requirements before creating a share
205
+     *
206
+     * @param IShare $share
207
+     * @throws \InvalidArgumentException
208
+     * @throws GenericShareException
209
+     *
210
+     * @suppress PhanUndeclaredClassMethod
211
+     */
212
+    protected function generalCreateChecks(IShare $share) {
213
+        if ($share->getShareType() === IShare::TYPE_USER) {
214
+            // We expect a valid user as sharedWith for user shares
215
+            if (!$this->userManager->userExists($share->getSharedWith())) {
216
+                throw new \InvalidArgumentException('SharedWith is not a valid user');
217
+            }
218
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
219
+            // We expect a valid group as sharedWith for group shares
220
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
221
+                throw new \InvalidArgumentException('SharedWith is not a valid group');
222
+            }
223
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
224
+            // No check for TYPE_EMAIL here as we have a recipient for them
225
+            if ($share->getSharedWith() !== null) {
226
+                throw new \InvalidArgumentException('SharedWith should be empty');
227
+            }
228
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
229
+            if ($share->getSharedWith() === null) {
230
+                throw new \InvalidArgumentException('SharedWith should not be empty');
231
+            }
232
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
233
+            if ($share->getSharedWith() === null) {
234
+                throw new \InvalidArgumentException('SharedWith should not be empty');
235
+            }
236
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
237
+            if ($share->getSharedWith() === null) {
238
+                throw new \InvalidArgumentException('SharedWith should not be empty');
239
+            }
240
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
241
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
242
+            if ($circle === null) {
243
+                throw new \InvalidArgumentException('SharedWith is not a valid circle');
244
+            }
245
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
246
+        } elseif ($share->getShareType() === IShare::TYPE_DECK) {
247
+        } elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
248
+        } else {
249
+            // We cannot handle other types yet
250
+            throw new \InvalidArgumentException('unknown share type');
251
+        }
252
+
253
+        // Verify the initiator of the share is set
254
+        if ($share->getSharedBy() === null) {
255
+            throw new \InvalidArgumentException('SharedBy should be set');
256
+        }
257
+
258
+        // Cannot share with yourself
259
+        if ($share->getShareType() === IShare::TYPE_USER &&
260
+            $share->getSharedWith() === $share->getSharedBy()) {
261
+            throw new \InvalidArgumentException('Cannot share with yourself');
262
+        }
263
+
264
+        // The path should be set
265
+        if ($share->getNode() === null) {
266
+            throw new \InvalidArgumentException('Path should be set');
267
+        }
268
+
269
+        // And it should be a file or a folder
270
+        if (!($share->getNode() instanceof \OCP\Files\File) &&
271
+            !($share->getNode() instanceof \OCP\Files\Folder)) {
272
+            throw new \InvalidArgumentException('Path should be either a file or a folder');
273
+        }
274
+
275
+        // And you cannot share your rootfolder
276
+        if ($this->userManager->userExists($share->getSharedBy())) {
277
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
278
+        } else {
279
+            $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
280
+        }
281
+        if ($userFolder->getId() === $share->getNode()->getId()) {
282
+            throw new \InvalidArgumentException('You cannot share your root folder');
283
+        }
284
+
285
+        // Check if we actually have share permissions
286
+        if (!$share->getNode()->isShareable()) {
287
+            $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]);
288
+            throw new GenericShareException($message_t, $message_t, 404);
289
+        }
290
+
291
+        // Permissions should be set
292
+        if ($share->getPermissions() === null) {
293
+            throw new \InvalidArgumentException('A share requires permissions');
294
+        }
295
+
296
+        $isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
297
+        $permissions = 0;
298
+
299
+        if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
300
+            $userMounts = array_filter($userFolder->getById($share->getNode()->getId()), function ($mount) {
301
+                // We need to filter since there might be other mountpoints that contain the file
302
+                // e.g. if the user has access to the same external storage that the file is originating from
303
+                return $mount->getStorage()->instanceOfStorage(ISharedStorage::class);
304
+            });
305
+            $userMount = array_shift($userMounts);
306
+            if ($userMount === null) {
307
+                throw new GenericShareException('Could not get proper share mount for ' . $share->getNode()->getId() . '. Failing since else the next calls are called with null');
308
+            }
309
+            $mount = $userMount->getMountPoint();
310
+            // When it's a reshare use the parent share permissions as maximum
311
+            $userMountPointId = $mount->getStorageRootId();
312
+            $userMountPoints = $userFolder->getById($userMountPointId);
313
+            $userMountPoint = array_shift($userMountPoints);
314
+
315
+            if ($userMountPoint === null) {
316
+                throw new GenericShareException('Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null');
317
+            }
318
+
319
+            /* Check if this is an incoming share */
320
+            $incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0);
321
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0));
322
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0));
323
+            $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0));
324
+
325
+            /** @var IShare[] $incomingShares */
326
+            if (!empty($incomingShares)) {
327
+                foreach ($incomingShares as $incomingShare) {
328
+                    $permissions |= $incomingShare->getPermissions();
329
+                }
330
+            }
331
+        } else {
332
+            /*
333 333
 			 * Quick fix for #23536
334 334
 			 * Non moveable mount points do not have update and delete permissions
335 335
 			 * while we 'most likely' do have that on the storage.
336 336
 			 */
337
-			$permissions = $share->getNode()->getPermissions();
338
-			if (!($share->getNode()->getMountPoint() instanceof MoveableMount)) {
339
-				$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
340
-			}
341
-		}
342
-
343
-		// Check that we do not share with more permissions than we have
344
-		if ($share->getPermissions() & ~$permissions) {
345
-			$path = $userFolder->getRelativePath($share->getNode()->getPath());
346
-			$message_t = $this->l->t('Cannot increase permissions of %s', [$path]);
347
-			throw new GenericShareException($message_t, $message_t, 404);
348
-		}
349
-
350
-
351
-		// Check that read permissions are always set
352
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
353
-		$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
354
-			|| $share->getShareType() === IShare::TYPE_EMAIL;
355
-		if (!$noReadPermissionRequired &&
356
-			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
357
-			throw new \InvalidArgumentException('Shares need at least read permissions');
358
-		}
359
-
360
-		if ($share->getNode() instanceof \OCP\Files\File) {
361
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
362
-				$message_t = $this->l->t('Files cannot be shared with delete permissions');
363
-				throw new GenericShareException($message_t);
364
-			}
365
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
366
-				$message_t = $this->l->t('Files cannot be shared with create permissions');
367
-				throw new GenericShareException($message_t);
368
-			}
369
-		}
370
-	}
371
-
372
-	/**
373
-	 * Validate if the expiration date fits the system settings
374
-	 *
375
-	 * @param IShare $share The share to validate the expiration date of
376
-	 * @return IShare The modified share object
377
-	 * @throws GenericShareException
378
-	 * @throws \InvalidArgumentException
379
-	 * @throws \Exception
380
-	 */
381
-	protected function validateExpirationDateInternal(IShare $share) {
382
-		$isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
383
-
384
-		$expirationDate = $share->getExpirationDate();
385
-
386
-		if ($expirationDate !== null) {
387
-			//Make sure the expiration date is a date
388
-			$expirationDate->setTime(0, 0, 0);
389
-
390
-			$date = new \DateTime();
391
-			$date->setTime(0, 0, 0);
392
-			if ($date >= $expirationDate) {
393
-				$message = $this->l->t('Expiration date is in the past');
394
-				throw new GenericShareException($message, $message, 404);
395
-			}
396
-		}
397
-
398
-		// If expiredate is empty set a default one if there is a default
399
-		$fullId = null;
400
-		try {
401
-			$fullId = $share->getFullId();
402
-		} catch (\UnexpectedValueException $e) {
403
-			// This is a new share
404
-		}
405
-
406
-		if ($isRemote) {
407
-			$defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
408
-			$defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
409
-			$configProp = 'remote_defaultExpDays';
410
-			$isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
411
-		} else {
412
-			$defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
413
-			$defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
414
-			$configProp = 'internal_defaultExpDays';
415
-			$isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
416
-		}
417
-		if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
418
-			$expirationDate = new \DateTime();
419
-			$expirationDate->setTime(0, 0, 0);
420
-
421
-			$days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
422
-			if ($days > $defaultExpireDays) {
423
-				$days = $defaultExpireDays;
424
-			}
425
-			$expirationDate->add(new \DateInterval('P' . $days . 'D'));
426
-		}
427
-
428
-		// If we enforce the expiration date check that is does not exceed
429
-		if ($isEnforced) {
430
-			if ($expirationDate === null) {
431
-				throw new \InvalidArgumentException('Expiration date is enforced');
432
-			}
433
-
434
-			$date = new \DateTime();
435
-			$date->setTime(0, 0, 0);
436
-			$date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
437
-			if ($date < $expirationDate) {
438
-				$message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays);
439
-				throw new GenericShareException($message, $message, 404);
440
-			}
441
-		}
442
-
443
-		$accepted = true;
444
-		$message = '';
445
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
446
-			'expirationDate' => &$expirationDate,
447
-			'accepted' => &$accepted,
448
-			'message' => &$message,
449
-			'passwordSet' => $share->getPassword() !== null,
450
-		]);
451
-
452
-		if (!$accepted) {
453
-			throw new \Exception($message);
454
-		}
455
-
456
-		$share->setExpirationDate($expirationDate);
457
-
458
-		return $share;
459
-	}
460
-
461
-	/**
462
-	 * Validate if the expiration date fits the system settings
463
-	 *
464
-	 * @param IShare $share The share to validate the expiration date of
465
-	 * @return IShare The modified share object
466
-	 * @throws GenericShareException
467
-	 * @throws \InvalidArgumentException
468
-	 * @throws \Exception
469
-	 */
470
-	protected function validateExpirationDateLink(IShare $share) {
471
-		$expirationDate = $share->getExpirationDate();
472
-
473
-		if ($expirationDate !== null) {
474
-			//Make sure the expiration date is a date
475
-			$expirationDate->setTime(0, 0, 0);
476
-
477
-			$date = new \DateTime();
478
-			$date->setTime(0, 0, 0);
479
-			if ($date >= $expirationDate) {
480
-				$message = $this->l->t('Expiration date is in the past');
481
-				throw new GenericShareException($message, $message, 404);
482
-			}
483
-		}
484
-
485
-		// If expiredate is empty set a default one if there is a default
486
-		$fullId = null;
487
-		try {
488
-			$fullId = $share->getFullId();
489
-		} catch (\UnexpectedValueException $e) {
490
-			// This is a new share
491
-		}
492
-
493
-		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
494
-			$expirationDate = new \DateTime();
495
-			$expirationDate->setTime(0, 0, 0);
496
-
497
-			$days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
498
-			if ($days > $this->shareApiLinkDefaultExpireDays()) {
499
-				$days = $this->shareApiLinkDefaultExpireDays();
500
-			}
501
-			$expirationDate->add(new \DateInterval('P' . $days . 'D'));
502
-		}
503
-
504
-		// If we enforce the expiration date check that is does not exceed
505
-		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
506
-			if ($expirationDate === null) {
507
-				throw new \InvalidArgumentException('Expiration date is enforced');
508
-			}
509
-
510
-			$date = new \DateTime();
511
-			$date->setTime(0, 0, 0);
512
-			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
513
-			if ($date < $expirationDate) {
514
-				$message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays());
515
-				throw new GenericShareException($message, $message, 404);
516
-			}
517
-		}
518
-
519
-		$accepted = true;
520
-		$message = '';
521
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
522
-			'expirationDate' => &$expirationDate,
523
-			'accepted' => &$accepted,
524
-			'message' => &$message,
525
-			'passwordSet' => $share->getPassword() !== null,
526
-		]);
527
-
528
-		if (!$accepted) {
529
-			throw new \Exception($message);
530
-		}
531
-
532
-		$share->setExpirationDate($expirationDate);
533
-
534
-		return $share;
535
-	}
536
-
537
-	/**
538
-	 * Check for pre share requirements for user shares
539
-	 *
540
-	 * @param IShare $share
541
-	 * @throws \Exception
542
-	 */
543
-	protected function userCreateChecks(IShare $share) {
544
-		// Check if we can share with group members only
545
-		if ($this->shareWithGroupMembersOnly()) {
546
-			$sharedBy = $this->userManager->get($share->getSharedBy());
547
-			$sharedWith = $this->userManager->get($share->getSharedWith());
548
-			// Verify we can share with this user
549
-			$groups = array_intersect(
550
-				$this->groupManager->getUserGroupIds($sharedBy),
551
-				$this->groupManager->getUserGroupIds($sharedWith)
552
-			);
553
-			if (empty($groups)) {
554
-				$message_t = $this->l->t('Sharing is only allowed with group members');
555
-				throw new \Exception($message_t);
556
-			}
557
-		}
558
-
559
-		/*
337
+            $permissions = $share->getNode()->getPermissions();
338
+            if (!($share->getNode()->getMountPoint() instanceof MoveableMount)) {
339
+                $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
340
+            }
341
+        }
342
+
343
+        // Check that we do not share with more permissions than we have
344
+        if ($share->getPermissions() & ~$permissions) {
345
+            $path = $userFolder->getRelativePath($share->getNode()->getPath());
346
+            $message_t = $this->l->t('Cannot increase permissions of %s', [$path]);
347
+            throw new GenericShareException($message_t, $message_t, 404);
348
+        }
349
+
350
+
351
+        // Check that read permissions are always set
352
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
353
+        $noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
354
+            || $share->getShareType() === IShare::TYPE_EMAIL;
355
+        if (!$noReadPermissionRequired &&
356
+            ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
357
+            throw new \InvalidArgumentException('Shares need at least read permissions');
358
+        }
359
+
360
+        if ($share->getNode() instanceof \OCP\Files\File) {
361
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
362
+                $message_t = $this->l->t('Files cannot be shared with delete permissions');
363
+                throw new GenericShareException($message_t);
364
+            }
365
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
366
+                $message_t = $this->l->t('Files cannot be shared with create permissions');
367
+                throw new GenericShareException($message_t);
368
+            }
369
+        }
370
+    }
371
+
372
+    /**
373
+     * Validate if the expiration date fits the system settings
374
+     *
375
+     * @param IShare $share The share to validate the expiration date of
376
+     * @return IShare The modified share object
377
+     * @throws GenericShareException
378
+     * @throws \InvalidArgumentException
379
+     * @throws \Exception
380
+     */
381
+    protected function validateExpirationDateInternal(IShare $share) {
382
+        $isRemote = $share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP;
383
+
384
+        $expirationDate = $share->getExpirationDate();
385
+
386
+        if ($expirationDate !== null) {
387
+            //Make sure the expiration date is a date
388
+            $expirationDate->setTime(0, 0, 0);
389
+
390
+            $date = new \DateTime();
391
+            $date->setTime(0, 0, 0);
392
+            if ($date >= $expirationDate) {
393
+                $message = $this->l->t('Expiration date is in the past');
394
+                throw new GenericShareException($message, $message, 404);
395
+            }
396
+        }
397
+
398
+        // If expiredate is empty set a default one if there is a default
399
+        $fullId = null;
400
+        try {
401
+            $fullId = $share->getFullId();
402
+        } catch (\UnexpectedValueException $e) {
403
+            // This is a new share
404
+        }
405
+
406
+        if ($isRemote) {
407
+            $defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
408
+            $defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
409
+            $configProp = 'remote_defaultExpDays';
410
+            $isEnforced = $this->shareApiRemoteDefaultExpireDateEnforced();
411
+        } else {
412
+            $defaultExpireDate = $this->shareApiInternalDefaultExpireDate();
413
+            $defaultExpireDays = $this->shareApiInternalDefaultExpireDays();
414
+            $configProp = 'internal_defaultExpDays';
415
+            $isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
416
+        }
417
+        if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
418
+            $expirationDate = new \DateTime();
419
+            $expirationDate->setTime(0, 0, 0);
420
+
421
+            $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
422
+            if ($days > $defaultExpireDays) {
423
+                $days = $defaultExpireDays;
424
+            }
425
+            $expirationDate->add(new \DateInterval('P' . $days . 'D'));
426
+        }
427
+
428
+        // If we enforce the expiration date check that is does not exceed
429
+        if ($isEnforced) {
430
+            if ($expirationDate === null) {
431
+                throw new \InvalidArgumentException('Expiration date is enforced');
432
+            }
433
+
434
+            $date = new \DateTime();
435
+            $date->setTime(0, 0, 0);
436
+            $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
437
+            if ($date < $expirationDate) {
438
+                $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays);
439
+                throw new GenericShareException($message, $message, 404);
440
+            }
441
+        }
442
+
443
+        $accepted = true;
444
+        $message = '';
445
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
446
+            'expirationDate' => &$expirationDate,
447
+            'accepted' => &$accepted,
448
+            'message' => &$message,
449
+            'passwordSet' => $share->getPassword() !== null,
450
+        ]);
451
+
452
+        if (!$accepted) {
453
+            throw new \Exception($message);
454
+        }
455
+
456
+        $share->setExpirationDate($expirationDate);
457
+
458
+        return $share;
459
+    }
460
+
461
+    /**
462
+     * Validate if the expiration date fits the system settings
463
+     *
464
+     * @param IShare $share The share to validate the expiration date of
465
+     * @return IShare The modified share object
466
+     * @throws GenericShareException
467
+     * @throws \InvalidArgumentException
468
+     * @throws \Exception
469
+     */
470
+    protected function validateExpirationDateLink(IShare $share) {
471
+        $expirationDate = $share->getExpirationDate();
472
+
473
+        if ($expirationDate !== null) {
474
+            //Make sure the expiration date is a date
475
+            $expirationDate->setTime(0, 0, 0);
476
+
477
+            $date = new \DateTime();
478
+            $date->setTime(0, 0, 0);
479
+            if ($date >= $expirationDate) {
480
+                $message = $this->l->t('Expiration date is in the past');
481
+                throw new GenericShareException($message, $message, 404);
482
+            }
483
+        }
484
+
485
+        // If expiredate is empty set a default one if there is a default
486
+        $fullId = null;
487
+        try {
488
+            $fullId = $share->getFullId();
489
+        } catch (\UnexpectedValueException $e) {
490
+            // This is a new share
491
+        }
492
+
493
+        if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
494
+            $expirationDate = new \DateTime();
495
+            $expirationDate->setTime(0, 0, 0);
496
+
497
+            $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', $this->shareApiLinkDefaultExpireDays());
498
+            if ($days > $this->shareApiLinkDefaultExpireDays()) {
499
+                $days = $this->shareApiLinkDefaultExpireDays();
500
+            }
501
+            $expirationDate->add(new \DateInterval('P' . $days . 'D'));
502
+        }
503
+
504
+        // If we enforce the expiration date check that is does not exceed
505
+        if ($this->shareApiLinkDefaultExpireDateEnforced()) {
506
+            if ($expirationDate === null) {
507
+                throw new \InvalidArgumentException('Expiration date is enforced');
508
+            }
509
+
510
+            $date = new \DateTime();
511
+            $date->setTime(0, 0, 0);
512
+            $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
513
+            if ($date < $expirationDate) {
514
+                $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays());
515
+                throw new GenericShareException($message, $message, 404);
516
+            }
517
+        }
518
+
519
+        $accepted = true;
520
+        $message = '';
521
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
522
+            'expirationDate' => &$expirationDate,
523
+            'accepted' => &$accepted,
524
+            'message' => &$message,
525
+            'passwordSet' => $share->getPassword() !== null,
526
+        ]);
527
+
528
+        if (!$accepted) {
529
+            throw new \Exception($message);
530
+        }
531
+
532
+        $share->setExpirationDate($expirationDate);
533
+
534
+        return $share;
535
+    }
536
+
537
+    /**
538
+     * Check for pre share requirements for user shares
539
+     *
540
+     * @param IShare $share
541
+     * @throws \Exception
542
+     */
543
+    protected function userCreateChecks(IShare $share) {
544
+        // Check if we can share with group members only
545
+        if ($this->shareWithGroupMembersOnly()) {
546
+            $sharedBy = $this->userManager->get($share->getSharedBy());
547
+            $sharedWith = $this->userManager->get($share->getSharedWith());
548
+            // Verify we can share with this user
549
+            $groups = array_intersect(
550
+                $this->groupManager->getUserGroupIds($sharedBy),
551
+                $this->groupManager->getUserGroupIds($sharedWith)
552
+            );
553
+            if (empty($groups)) {
554
+                $message_t = $this->l->t('Sharing is only allowed with group members');
555
+                throw new \Exception($message_t);
556
+            }
557
+        }
558
+
559
+        /*
560 560
 		 * TODO: Could be costly, fix
561 561
 		 *
562 562
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
563 563
 		 */
564
-		$provider = $this->factory->getProviderForType(IShare::TYPE_USER);
565
-		$existingShares = $provider->getSharesByPath($share->getNode());
566
-		foreach ($existingShares as $existingShare) {
567
-			// Ignore if it is the same share
568
-			try {
569
-				if ($existingShare->getFullId() === $share->getFullId()) {
570
-					continue;
571
-				}
572
-			} catch (\UnexpectedValueException $e) {
573
-				//Shares are not identical
574
-			}
575
-
576
-			// Identical share already exists
577
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
578
-				$message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
579
-				throw new AlreadySharedException($message, $existingShare);
580
-			}
581
-
582
-			// The share is already shared with this user via a group share
583
-			if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
584
-				$group = $this->groupManager->get($existingShare->getSharedWith());
585
-				if (!is_null($group)) {
586
-					$user = $this->userManager->get($share->getSharedWith());
587
-
588
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
589
-						$message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
590
-						throw new AlreadySharedException($message, $existingShare);
591
-					}
592
-				}
593
-			}
594
-		}
595
-	}
596
-
597
-	/**
598
-	 * Check for pre share requirements for group shares
599
-	 *
600
-	 * @param IShare $share
601
-	 * @throws \Exception
602
-	 */
603
-	protected function groupCreateChecks(IShare $share) {
604
-		// Verify group shares are allowed
605
-		if (!$this->allowGroupSharing()) {
606
-			throw new \Exception('Group sharing is now allowed');
607
-		}
608
-
609
-		// Verify if the user can share with this group
610
-		if ($this->shareWithGroupMembersOnly()) {
611
-			$sharedBy = $this->userManager->get($share->getSharedBy());
612
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
613
-			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
614
-				throw new \Exception('Sharing is only allowed within your own groups');
615
-			}
616
-		}
617
-
618
-		/*
564
+        $provider = $this->factory->getProviderForType(IShare::TYPE_USER);
565
+        $existingShares = $provider->getSharesByPath($share->getNode());
566
+        foreach ($existingShares as $existingShare) {
567
+            // Ignore if it is the same share
568
+            try {
569
+                if ($existingShare->getFullId() === $share->getFullId()) {
570
+                    continue;
571
+                }
572
+            } catch (\UnexpectedValueException $e) {
573
+                //Shares are not identical
574
+            }
575
+
576
+            // Identical share already exists
577
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
578
+                $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
579
+                throw new AlreadySharedException($message, $existingShare);
580
+            }
581
+
582
+            // The share is already shared with this user via a group share
583
+            if ($existingShare->getShareType() === IShare::TYPE_GROUP) {
584
+                $group = $this->groupManager->get($existingShare->getSharedWith());
585
+                if (!is_null($group)) {
586
+                    $user = $this->userManager->get($share->getSharedWith());
587
+
588
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
589
+                        $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
590
+                        throw new AlreadySharedException($message, $existingShare);
591
+                    }
592
+                }
593
+            }
594
+        }
595
+    }
596
+
597
+    /**
598
+     * Check for pre share requirements for group shares
599
+     *
600
+     * @param IShare $share
601
+     * @throws \Exception
602
+     */
603
+    protected function groupCreateChecks(IShare $share) {
604
+        // Verify group shares are allowed
605
+        if (!$this->allowGroupSharing()) {
606
+            throw new \Exception('Group sharing is now allowed');
607
+        }
608
+
609
+        // Verify if the user can share with this group
610
+        if ($this->shareWithGroupMembersOnly()) {
611
+            $sharedBy = $this->userManager->get($share->getSharedBy());
612
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
613
+            if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
614
+                throw new \Exception('Sharing is only allowed within your own groups');
615
+            }
616
+        }
617
+
618
+        /*
619 619
 		 * TODO: Could be costly, fix
620 620
 		 *
621 621
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
622 622
 		 */
623
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
624
-		$existingShares = $provider->getSharesByPath($share->getNode());
625
-		foreach ($existingShares as $existingShare) {
626
-			try {
627
-				if ($existingShare->getFullId() === $share->getFullId()) {
628
-					continue;
629
-				}
630
-			} catch (\UnexpectedValueException $e) {
631
-				//It is a new share so just continue
632
-			}
633
-
634
-			if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
635
-				throw new AlreadySharedException('Path is already shared with this group', $existingShare);
636
-			}
637
-		}
638
-	}
639
-
640
-	/**
641
-	 * Check for pre share requirements for link shares
642
-	 *
643
-	 * @param IShare $share
644
-	 * @throws \Exception
645
-	 */
646
-	protected function linkCreateChecks(IShare $share) {
647
-		// Are link shares allowed?
648
-		if (!$this->shareApiAllowLinks()) {
649
-			throw new \Exception('Link sharing is not allowed');
650
-		}
651
-
652
-		// Check if public upload is allowed
653
-		if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
654
-			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
655
-			throw new \InvalidArgumentException('Public upload is not allowed');
656
-		}
657
-	}
658
-
659
-	/**
660
-	 * To make sure we don't get invisible link shares we set the parent
661
-	 * of a link if it is a reshare. This is a quick word around
662
-	 * until we can properly display multiple link shares in the UI
663
-	 *
664
-	 * See: https://github.com/owncloud/core/issues/22295
665
-	 *
666
-	 * FIXME: Remove once multiple link shares can be properly displayed
667
-	 *
668
-	 * @param IShare $share
669
-	 */
670
-	protected function setLinkParent(IShare $share) {
671
-		// No sense in checking if the method is not there.
672
-		if (method_exists($share, 'setParent')) {
673
-			$storage = $share->getNode()->getStorage();
674
-			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
675
-				/** @var \OCA\Files_Sharing\SharedStorage $storage */
676
-				$share->setParent($storage->getShareId());
677
-			}
678
-		}
679
-	}
680
-
681
-	/**
682
-	 * @param File|Folder $path
683
-	 */
684
-	protected function pathCreateChecks($path) {
685
-		// Make sure that we do not share a path that contains a shared mountpoint
686
-		if ($path instanceof \OCP\Files\Folder) {
687
-			$mounts = $this->mountManager->findIn($path->getPath());
688
-			foreach ($mounts as $mount) {
689
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
690
-					throw new \InvalidArgumentException('Path contains files shared with you');
691
-				}
692
-			}
693
-		}
694
-	}
695
-
696
-	/**
697
-	 * Check if the user that is sharing can actually share
698
-	 *
699
-	 * @param IShare $share
700
-	 * @throws \Exception
701
-	 */
702
-	protected function canShare(IShare $share) {
703
-		if (!$this->shareApiEnabled()) {
704
-			throw new \Exception('Sharing is disabled');
705
-		}
706
-
707
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
708
-			throw new \Exception('Sharing is disabled for you');
709
-		}
710
-	}
711
-
712
-	/**
713
-	 * Share a path
714
-	 *
715
-	 * @param IShare $share
716
-	 * @return IShare The share object
717
-	 * @throws \Exception
718
-	 *
719
-	 * TODO: handle link share permissions or check them
720
-	 */
721
-	public function createShare(IShare $share) {
722
-		$this->canShare($share);
723
-
724
-		$this->generalCreateChecks($share);
725
-
726
-		// Verify if there are any issues with the path
727
-		$this->pathCreateChecks($share->getNode());
728
-
729
-		/*
623
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
624
+        $existingShares = $provider->getSharesByPath($share->getNode());
625
+        foreach ($existingShares as $existingShare) {
626
+            try {
627
+                if ($existingShare->getFullId() === $share->getFullId()) {
628
+                    continue;
629
+                }
630
+            } catch (\UnexpectedValueException $e) {
631
+                //It is a new share so just continue
632
+            }
633
+
634
+            if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
635
+                throw new AlreadySharedException('Path is already shared with this group', $existingShare);
636
+            }
637
+        }
638
+    }
639
+
640
+    /**
641
+     * Check for pre share requirements for link shares
642
+     *
643
+     * @param IShare $share
644
+     * @throws \Exception
645
+     */
646
+    protected function linkCreateChecks(IShare $share) {
647
+        // Are link shares allowed?
648
+        if (!$this->shareApiAllowLinks()) {
649
+            throw new \Exception('Link sharing is not allowed');
650
+        }
651
+
652
+        // Check if public upload is allowed
653
+        if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
654
+            ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
655
+            throw new \InvalidArgumentException('Public upload is not allowed');
656
+        }
657
+    }
658
+
659
+    /**
660
+     * To make sure we don't get invisible link shares we set the parent
661
+     * of a link if it is a reshare. This is a quick word around
662
+     * until we can properly display multiple link shares in the UI
663
+     *
664
+     * See: https://github.com/owncloud/core/issues/22295
665
+     *
666
+     * FIXME: Remove once multiple link shares can be properly displayed
667
+     *
668
+     * @param IShare $share
669
+     */
670
+    protected function setLinkParent(IShare $share) {
671
+        // No sense in checking if the method is not there.
672
+        if (method_exists($share, 'setParent')) {
673
+            $storage = $share->getNode()->getStorage();
674
+            if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
675
+                /** @var \OCA\Files_Sharing\SharedStorage $storage */
676
+                $share->setParent($storage->getShareId());
677
+            }
678
+        }
679
+    }
680
+
681
+    /**
682
+     * @param File|Folder $path
683
+     */
684
+    protected function pathCreateChecks($path) {
685
+        // Make sure that we do not share a path that contains a shared mountpoint
686
+        if ($path instanceof \OCP\Files\Folder) {
687
+            $mounts = $this->mountManager->findIn($path->getPath());
688
+            foreach ($mounts as $mount) {
689
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
690
+                    throw new \InvalidArgumentException('Path contains files shared with you');
691
+                }
692
+            }
693
+        }
694
+    }
695
+
696
+    /**
697
+     * Check if the user that is sharing can actually share
698
+     *
699
+     * @param IShare $share
700
+     * @throws \Exception
701
+     */
702
+    protected function canShare(IShare $share) {
703
+        if (!$this->shareApiEnabled()) {
704
+            throw new \Exception('Sharing is disabled');
705
+        }
706
+
707
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
708
+            throw new \Exception('Sharing is disabled for you');
709
+        }
710
+    }
711
+
712
+    /**
713
+     * Share a path
714
+     *
715
+     * @param IShare $share
716
+     * @return IShare The share object
717
+     * @throws \Exception
718
+     *
719
+     * TODO: handle link share permissions or check them
720
+     */
721
+    public function createShare(IShare $share) {
722
+        $this->canShare($share);
723
+
724
+        $this->generalCreateChecks($share);
725
+
726
+        // Verify if there are any issues with the path
727
+        $this->pathCreateChecks($share->getNode());
728
+
729
+        /*
730 730
 		 * On creation of a share the owner is always the owner of the path
731 731
 		 * Except for mounted federated shares.
732 732
 		 */
733
-		$storage = $share->getNode()->getStorage();
734
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
735
-			$parent = $share->getNode()->getParent();
736
-			while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
737
-				$parent = $parent->getParent();
738
-			}
739
-			$share->setShareOwner($parent->getOwner()->getUID());
740
-		} else {
741
-			if ($share->getNode()->getOwner()) {
742
-				$share->setShareOwner($share->getNode()->getOwner()->getUID());
743
-			} else {
744
-				$share->setShareOwner($share->getSharedBy());
745
-			}
746
-		}
747
-
748
-		try {
749
-			// Verify share type
750
-			if ($share->getShareType() === IShare::TYPE_USER) {
751
-				$this->userCreateChecks($share);
752
-
753
-				// Verify the expiration date
754
-				$share = $this->validateExpirationDateInternal($share);
755
-			} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
756
-				$this->groupCreateChecks($share);
757
-
758
-				// Verify the expiration date
759
-				$share = $this->validateExpirationDateInternal($share);
760
-			} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
761
-				//Verify the expiration date
762
-				$share = $this->validateExpirationDateInternal($share);
763
-			} elseif ($share->getShareType() === IShare::TYPE_LINK
764
-				|| $share->getShareType() === IShare::TYPE_EMAIL) {
765
-				$this->linkCreateChecks($share);
766
-				$this->setLinkParent($share);
767
-
768
-				// For now ignore a set token.
769
-				$share->setToken(
770
-					$this->secureRandom->generate(
771
-						\OC\Share\Constants::TOKEN_LENGTH,
772
-						\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
773
-					)
774
-				);
775
-
776
-				// Verify the expiration date
777
-				$share = $this->validateExpirationDateLink($share);
778
-
779
-				// Verify the password
780
-				$this->verifyPassword($share->getPassword());
781
-
782
-				// If a password is set. Hash it!
783
-				if ($share->getShareType() === IShare::TYPE_LINK
784
-					&& $share->getPassword() !== null) {
785
-					$share->setPassword($this->hasher->hash($share->getPassword()));
786
-				}
787
-			}
788
-
789
-			// Cannot share with the owner
790
-			if ($share->getShareType() === IShare::TYPE_USER &&
791
-				$share->getSharedWith() === $share->getShareOwner()) {
792
-				throw new \InvalidArgumentException('Cannot share with the share owner');
793
-			}
794
-
795
-			// Generate the target
796
-			$defaultShareFolder = $this->config->getSystemValue('share_folder', '/');
797
-			$allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
798
-			if ($allowCustomShareFolder) {
799
-				$shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $defaultShareFolder);
800
-			} else {
801
-				$shareFolder = $defaultShareFolder;
802
-			}
803
-
804
-			$target = $shareFolder . '/' . $share->getNode()->getName();
805
-			$target = \OC\Files\Filesystem::normalizePath($target);
806
-			$share->setTarget($target);
807
-
808
-			// Pre share event
809
-			$event = new GenericEvent($share);
810
-			$this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
811
-			if ($event->isPropagationStopped() && $event->hasArgument('error')) {
812
-				throw new \Exception($event->getArgument('error'));
813
-			}
814
-
815
-			$oldShare = $share;
816
-			$provider = $this->factory->getProviderForType($share->getShareType());
817
-			$share = $provider->create($share);
818
-
819
-			// Reuse the node we already have
820
-			$share->setNode($oldShare->getNode());
821
-
822
-			// Reset the target if it is null for the new share
823
-			if ($share->getTarget() === '') {
824
-				$share->setTarget($target);
825
-			}
826
-		} catch (AlreadySharedException $e) {
827
-			// if a share for the same target already exists, dont create a new one, but do trigger the hooks and notifications again
828
-			$oldShare = $share;
829
-
830
-			// Reuse the node we already have
831
-			$share = $e->getExistingShare();
832
-			$share->setNode($oldShare->getNode());
833
-		}
834
-
835
-		// Post share event
836
-		$event = new GenericEvent($share);
837
-		$this->legacyDispatcher->dispatch('OCP\Share::postShare', $event);
838
-
839
-		$this->dispatcher->dispatchTyped(new Share\Events\ShareCreatedEvent($share));
840
-
841
-		if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)
842
-			&& $share->getShareType() === IShare::TYPE_USER) {
843
-			$mailSend = $share->getMailSend();
844
-			if ($mailSend === true) {
845
-				$user = $this->userManager->get($share->getSharedWith());
846
-				if ($user !== null) {
847
-					$emailAddress = $user->getEMailAddress();
848
-					if ($emailAddress !== null && $emailAddress !== '') {
849
-						$userLang = $this->l10nFactory->getUserLanguage($user);
850
-						$l = $this->l10nFactory->get('lib', $userLang);
851
-						$this->sendMailNotification(
852
-							$l,
853
-							$share->getNode()->getName(),
854
-							$this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
855
-							$share->getSharedBy(),
856
-							$emailAddress,
857
-							$share->getExpirationDate(),
858
-							$share->getNote()
859
-						);
860
-						$this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
861
-					} else {
862
-						$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
863
-					}
864
-				} else {
865
-					$this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
866
-				}
867
-			} else {
868
-				$this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
869
-			}
870
-		}
871
-
872
-		return $share;
873
-	}
874
-
875
-	/**
876
-	 * Send mail notifications
877
-	 *
878
-	 * This method will catch and log mail transmission errors
879
-	 *
880
-	 * @param IL10N $l Language of the recipient
881
-	 * @param string $filename file/folder name
882
-	 * @param string $link link to the file/folder
883
-	 * @param string $initiator user ID of share sender
884
-	 * @param string $shareWith email address of share receiver
885
-	 * @param \DateTime|null $expiration
886
-	 */
887
-	protected function sendMailNotification(IL10N $l,
888
-											$filename,
889
-											$link,
890
-											$initiator,
891
-											$shareWith,
892
-											\DateTime $expiration = null,
893
-											$note = '') {
894
-		$initiatorUser = $this->userManager->get($initiator);
895
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
896
-
897
-		$message = $this->mailer->createMessage();
898
-
899
-		$emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
900
-			'filename' => $filename,
901
-			'link' => $link,
902
-			'initiator' => $initiatorDisplayName,
903
-			'expiration' => $expiration,
904
-			'shareWith' => $shareWith,
905
-		]);
906
-
907
-		$emailTemplate->setSubject($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]));
908
-		$emailTemplate->addHeader();
909
-		$emailTemplate->addHeading($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]), false);
910
-		$text = $l->t('%1$s shared »%2$s« with you.', [$initiatorDisplayName, $filename]);
911
-
912
-		if ($note !== '') {
913
-			$emailTemplate->addBodyText(htmlspecialchars($note), $note);
914
-		}
915
-
916
-		$emailTemplate->addBodyText(
917
-			htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
918
-			$text
919
-		);
920
-		$emailTemplate->addBodyButton(
921
-			$l->t('Open »%s«', [$filename]),
922
-			$link
923
-		);
924
-
925
-		$message->setTo([$shareWith]);
926
-
927
-		// The "From" contains the sharers name
928
-		$instanceName = $this->defaults->getName();
929
-		$senderName = $l->t(
930
-			'%1$s via %2$s',
931
-			[
932
-				$initiatorDisplayName,
933
-				$instanceName,
934
-			]
935
-		);
936
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]);
937
-
938
-		// The "Reply-To" is set to the sharer if an mail address is configured
939
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
940
-		$initiatorEmail = $initiatorUser->getEMailAddress();
941
-		if ($initiatorEmail !== null) {
942
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
943
-			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan($l->getLanguageCode()) !== '' ? ' - ' . $this->defaults->getSlogan($l->getLanguageCode()) : ''));
944
-		} else {
945
-			$emailTemplate->addFooter('', $l->getLanguageCode());
946
-		}
947
-
948
-		$message->useTemplate($emailTemplate);
949
-		try {
950
-			$failedRecipients = $this->mailer->send($message);
951
-			if (!empty($failedRecipients)) {
952
-				$this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
953
-				return;
954
-			}
955
-		} catch (\Exception $e) {
956
-			$this->logger->error('Share notification mail could not be sent', ['exception' => $e]);
957
-		}
958
-	}
959
-
960
-	/**
961
-	 * Update a share
962
-	 *
963
-	 * @param IShare $share
964
-	 * @return IShare The share object
965
-	 * @throws \InvalidArgumentException
966
-	 */
967
-	public function updateShare(IShare $share) {
968
-		$expirationDateUpdated = false;
969
-
970
-		$this->canShare($share);
971
-
972
-		try {
973
-			$originalShare = $this->getShareById($share->getFullId());
974
-		} catch (\UnexpectedValueException $e) {
975
-			throw new \InvalidArgumentException('Share does not have a full id');
976
-		}
977
-
978
-		// We cannot change the share type!
979
-		if ($share->getShareType() !== $originalShare->getShareType()) {
980
-			throw new \InvalidArgumentException('Cannot change share type');
981
-		}
982
-
983
-		// We can only change the recipient on user shares
984
-		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
985
-			$share->getShareType() !== IShare::TYPE_USER) {
986
-			throw new \InvalidArgumentException('Can only update recipient on user shares');
987
-		}
988
-
989
-		// Cannot share with the owner
990
-		if ($share->getShareType() === IShare::TYPE_USER &&
991
-			$share->getSharedWith() === $share->getShareOwner()) {
992
-			throw new \InvalidArgumentException('Cannot share with the share owner');
993
-		}
994
-
995
-		$this->generalCreateChecks($share);
996
-
997
-		if ($share->getShareType() === IShare::TYPE_USER) {
998
-			$this->userCreateChecks($share);
999
-
1000
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1001
-				//Verify the expiration date
1002
-				$this->validateExpirationDateInternal($share);
1003
-				$expirationDateUpdated = true;
1004
-			}
1005
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1006
-			$this->groupCreateChecks($share);
1007
-
1008
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1009
-				//Verify the expiration date
1010
-				$this->validateExpirationDateInternal($share);
1011
-				$expirationDateUpdated = true;
1012
-			}
1013
-		} elseif ($share->getShareType() === IShare::TYPE_LINK
1014
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1015
-			$this->linkCreateChecks($share);
1016
-
1017
-			// The new password is not set again if it is the same as the old
1018
-			// one, unless when switching from sending by Talk to sending by
1019
-			// mail.
1020
-			$plainTextPassword = $share->getPassword();
1021
-			$updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
1022
-
1023
-			/**
1024
-			 * Cannot enable the getSendPasswordByTalk if there is no password set
1025
-			 */
1026
-			if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
1027
-				throw new \InvalidArgumentException('Cannot enable sending the password by Talk with an empty password');
1028
-			}
1029
-
1030
-			/**
1031
-			 * If we're in a mail share, we need to force a password change
1032
-			 * as either the user is not aware of the password or is already (received by mail)
1033
-			 * Thus the SendPasswordByTalk feature would not make sense
1034
-			 */
1035
-			if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
1036
-				if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
1037
-					throw new \InvalidArgumentException('Cannot enable sending the password by Talk without setting a new password');
1038
-				}
1039
-				if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
1040
-					throw new \InvalidArgumentException('Cannot disable sending the password by Talk without setting a new password');
1041
-				}
1042
-			}
1043
-
1044
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1045
-				// Verify the expiration date
1046
-				$this->validateExpirationDateLink($share);
1047
-				$expirationDateUpdated = true;
1048
-			}
1049
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
1050
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1051
-				//Verify the expiration date
1052
-				$this->validateExpirationDateInternal($share);
1053
-				$expirationDateUpdated = true;
1054
-			}
1055
-		}
1056
-
1057
-		$this->pathCreateChecks($share->getNode());
1058
-
1059
-		// Now update the share!
1060
-		$provider = $this->factory->getProviderForType($share->getShareType());
1061
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
1062
-			$share = $provider->update($share, $plainTextPassword);
1063
-		} else {
1064
-			$share = $provider->update($share);
1065
-		}
1066
-
1067
-		if ($expirationDateUpdated === true) {
1068
-			\OC_Hook::emit(Share::class, 'post_set_expiration_date', [
1069
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1070
-				'itemSource' => $share->getNode()->getId(),
1071
-				'date' => $share->getExpirationDate(),
1072
-				'uidOwner' => $share->getSharedBy(),
1073
-			]);
1074
-		}
1075
-
1076
-		if ($share->getPassword() !== $originalShare->getPassword()) {
1077
-			\OC_Hook::emit(Share::class, 'post_update_password', [
1078
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1079
-				'itemSource' => $share->getNode()->getId(),
1080
-				'uidOwner' => $share->getSharedBy(),
1081
-				'token' => $share->getToken(),
1082
-				'disabled' => is_null($share->getPassword()),
1083
-			]);
1084
-		}
1085
-
1086
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
1087
-			if ($this->userManager->userExists($share->getShareOwner())) {
1088
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
1089
-			} else {
1090
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1091
-			}
1092
-			\OC_Hook::emit(Share::class, 'post_update_permissions', [
1093
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1094
-				'itemSource' => $share->getNode()->getId(),
1095
-				'shareType' => $share->getShareType(),
1096
-				'shareWith' => $share->getSharedWith(),
1097
-				'uidOwner' => $share->getSharedBy(),
1098
-				'permissions' => $share->getPermissions(),
1099
-				'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
1100
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
1101
-			]);
1102
-		}
1103
-
1104
-		return $share;
1105
-	}
1106
-
1107
-	/**
1108
-	 * Accept a share.
1109
-	 *
1110
-	 * @param IShare $share
1111
-	 * @param string $recipientId
1112
-	 * @return IShare The share object
1113
-	 * @throws \InvalidArgumentException
1114
-	 * @since 9.0.0
1115
-	 */
1116
-	public function acceptShare(IShare $share, string $recipientId): IShare {
1117
-		[$providerId,] = $this->splitFullId($share->getFullId());
1118
-		$provider = $this->factory->getProvider($providerId);
1119
-
1120
-		if (!method_exists($provider, 'acceptShare')) {
1121
-			// TODO FIX ME
1122
-			throw new \InvalidArgumentException('Share provider does not support accepting');
1123
-		}
1124
-		$provider->acceptShare($share, $recipientId);
1125
-		$event = new GenericEvent($share);
1126
-		$this->legacyDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
1127
-
1128
-		return $share;
1129
-	}
1130
-
1131
-	/**
1132
-	 * Updates the password of the given share if it is not the same as the
1133
-	 * password of the original share.
1134
-	 *
1135
-	 * @param IShare $share the share to update its password.
1136
-	 * @param IShare $originalShare the original share to compare its
1137
-	 *        password with.
1138
-	 * @return boolean whether the password was updated or not.
1139
-	 */
1140
-	private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
1141
-		$passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
1142
-			(($share->getPassword() !== null && $originalShare->getPassword() === null) ||
1143
-				($share->getPassword() === null && $originalShare->getPassword() !== null) ||
1144
-				($share->getPassword() !== null && $originalShare->getPassword() !== null &&
1145
-					!$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
1146
-
1147
-		// Password updated.
1148
-		if ($passwordsAreDifferent) {
1149
-			//Verify the password
1150
-			$this->verifyPassword($share->getPassword());
1151
-
1152
-			// If a password is set. Hash it!
1153
-			if (!empty($share->getPassword())) {
1154
-				$share->setPassword($this->hasher->hash($share->getPassword()));
1155
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
1156
-					// Shares shared by email have temporary passwords
1157
-					$this->setSharePasswordExpirationTime($share);
1158
-				}
1159
-
1160
-				return true;
1161
-			} else {
1162
-				// Empty string and null are seen as NOT password protected
1163
-				$share->setPassword(null);
1164
-				if ($share->getShareType() === IShare::TYPE_EMAIL) {
1165
-					$share->setPasswordExpirationTime(null);
1166
-				}
1167
-				return true;
1168
-			}
1169
-		} else {
1170
-			// Reset the password to the original one, as it is either the same
1171
-			// as the "new" password or a hashed version of it.
1172
-			$share->setPassword($originalShare->getPassword());
1173
-		}
1174
-
1175
-		return false;
1176
-	}
1177
-
1178
-	/**
1179
-	 * Set the share's password expiration time
1180
-	 */
1181
-	private function setSharePasswordExpirationTime(IShare $share): void {
1182
-		if (!$this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false)) {
1183
-			// Sets password expiration date to NULL
1184
-			$share->setPasswordExpirationTime();
1185
-			return;
1186
-		}
1187
-		// Sets password expiration date
1188
-		$expirationTime = null;
1189
-		$now = new \DateTime();
1190
-		$expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1191
-		$expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1192
-		$share->setPasswordExpirationTime($expirationTime);
1193
-	}
1194
-
1195
-
1196
-	/**
1197
-	 * Delete all the children of this share
1198
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
1199
-	 *
1200
-	 * @param IShare $share
1201
-	 * @return IShare[] List of deleted shares
1202
-	 */
1203
-	protected function deleteChildren(IShare $share) {
1204
-		$deletedShares = [];
1205
-
1206
-		$provider = $this->factory->getProviderForType($share->getShareType());
1207
-
1208
-		foreach ($provider->getChildren($share) as $child) {
1209
-			$deletedChildren = $this->deleteChildren($child);
1210
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
1211
-
1212
-			$provider->delete($child);
1213
-			$this->dispatcher->dispatchTyped(new Share\Events\ShareDeletedEvent($child));
1214
-			$deletedShares[] = $child;
1215
-		}
1216
-
1217
-		return $deletedShares;
1218
-	}
1219
-
1220
-	/**
1221
-	 * Delete a share
1222
-	 *
1223
-	 * @param IShare $share
1224
-	 * @throws ShareNotFound
1225
-	 * @throws \InvalidArgumentException
1226
-	 */
1227
-	public function deleteShare(IShare $share) {
1228
-		try {
1229
-			$share->getFullId();
1230
-		} catch (\UnexpectedValueException $e) {
1231
-			throw new \InvalidArgumentException('Share does not have a full id');
1232
-		}
1233
-
1234
-		$event = new GenericEvent($share);
1235
-		$this->legacyDispatcher->dispatch('OCP\Share::preUnshare', $event);
1236
-
1237
-		// Get all children and delete them as well
1238
-		$deletedShares = $this->deleteChildren($share);
1239
-
1240
-		// Do the actual delete
1241
-		$provider = $this->factory->getProviderForType($share->getShareType());
1242
-		$provider->delete($share);
1243
-
1244
-		$this->dispatcher->dispatchTyped(new Share\Events\ShareDeletedEvent($share));
1245
-
1246
-		// All the deleted shares caused by this delete
1247
-		$deletedShares[] = $share;
1248
-
1249
-		// Emit post hook
1250
-		$event->setArgument('deletedShares', $deletedShares);
1251
-		$this->legacyDispatcher->dispatch('OCP\Share::postUnshare', $event);
1252
-	}
1253
-
1254
-
1255
-	/**
1256
-	 * Unshare a file as the recipient.
1257
-	 * This can be different from a regular delete for example when one of
1258
-	 * the users in a groups deletes that share. But the provider should
1259
-	 * handle this.
1260
-	 *
1261
-	 * @param IShare $share
1262
-	 * @param string $recipientId
1263
-	 */
1264
-	public function deleteFromSelf(IShare $share, $recipientId) {
1265
-		[$providerId,] = $this->splitFullId($share->getFullId());
1266
-		$provider = $this->factory->getProvider($providerId);
1267
-
1268
-		$provider->deleteFromSelf($share, $recipientId);
1269
-		$event = new GenericEvent($share);
1270
-		$this->legacyDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
1271
-	}
1272
-
1273
-	public function restoreShare(IShare $share, string $recipientId): IShare {
1274
-		[$providerId,] = $this->splitFullId($share->getFullId());
1275
-		$provider = $this->factory->getProvider($providerId);
1276
-
1277
-		return $provider->restore($share, $recipientId);
1278
-	}
1279
-
1280
-	/**
1281
-	 * @inheritdoc
1282
-	 */
1283
-	public function moveShare(IShare $share, $recipientId) {
1284
-		if ($share->getShareType() === IShare::TYPE_LINK
1285
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
1286
-			throw new \InvalidArgumentException('Cannot change target of link share');
1287
-		}
1288
-
1289
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1290
-			throw new \InvalidArgumentException('Invalid recipient');
1291
-		}
1292
-
1293
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1294
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1295
-			if (is_null($sharedWith)) {
1296
-				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
1297
-			}
1298
-			$recipient = $this->userManager->get($recipientId);
1299
-			if (!$sharedWith->inGroup($recipient)) {
1300
-				throw new \InvalidArgumentException('Invalid recipient');
1301
-			}
1302
-		}
1303
-
1304
-		[$providerId,] = $this->splitFullId($share->getFullId());
1305
-		$provider = $this->factory->getProvider($providerId);
1306
-
1307
-		return $provider->move($share, $recipientId);
1308
-	}
1309
-
1310
-	public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1311
-		$providers = $this->factory->getAllProviders();
1312
-
1313
-		return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) {
1314
-			$newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow);
1315
-			foreach ($newShares as $fid => $data) {
1316
-				if (!isset($shares[$fid])) {
1317
-					$shares[$fid] = [];
1318
-				}
1319
-
1320
-				$shares[$fid] = array_merge($shares[$fid], $data);
1321
-			}
1322
-			return $shares;
1323
-		}, []);
1324
-	}
1325
-
1326
-	/**
1327
-	 * @inheritdoc
1328
-	 */
1329
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1330
-		if ($path !== null &&
1331
-			!($path instanceof \OCP\Files\File) &&
1332
-			!($path instanceof \OCP\Files\Folder)) {
1333
-			throw new \InvalidArgumentException('invalid path');
1334
-		}
1335
-
1336
-		try {
1337
-			$provider = $this->factory->getProviderForType($shareType);
1338
-		} catch (ProviderException $e) {
1339
-			return [];
1340
-		}
1341
-
1342
-		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1343
-
1344
-		/*
733
+        $storage = $share->getNode()->getStorage();
734
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
735
+            $parent = $share->getNode()->getParent();
736
+            while ($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
737
+                $parent = $parent->getParent();
738
+            }
739
+            $share->setShareOwner($parent->getOwner()->getUID());
740
+        } else {
741
+            if ($share->getNode()->getOwner()) {
742
+                $share->setShareOwner($share->getNode()->getOwner()->getUID());
743
+            } else {
744
+                $share->setShareOwner($share->getSharedBy());
745
+            }
746
+        }
747
+
748
+        try {
749
+            // Verify share type
750
+            if ($share->getShareType() === IShare::TYPE_USER) {
751
+                $this->userCreateChecks($share);
752
+
753
+                // Verify the expiration date
754
+                $share = $this->validateExpirationDateInternal($share);
755
+            } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
756
+                $this->groupCreateChecks($share);
757
+
758
+                // Verify the expiration date
759
+                $share = $this->validateExpirationDateInternal($share);
760
+            } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
761
+                //Verify the expiration date
762
+                $share = $this->validateExpirationDateInternal($share);
763
+            } elseif ($share->getShareType() === IShare::TYPE_LINK
764
+                || $share->getShareType() === IShare::TYPE_EMAIL) {
765
+                $this->linkCreateChecks($share);
766
+                $this->setLinkParent($share);
767
+
768
+                // For now ignore a set token.
769
+                $share->setToken(
770
+                    $this->secureRandom->generate(
771
+                        \OC\Share\Constants::TOKEN_LENGTH,
772
+                        \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
773
+                    )
774
+                );
775
+
776
+                // Verify the expiration date
777
+                $share = $this->validateExpirationDateLink($share);
778
+
779
+                // Verify the password
780
+                $this->verifyPassword($share->getPassword());
781
+
782
+                // If a password is set. Hash it!
783
+                if ($share->getShareType() === IShare::TYPE_LINK
784
+                    && $share->getPassword() !== null) {
785
+                    $share->setPassword($this->hasher->hash($share->getPassword()));
786
+                }
787
+            }
788
+
789
+            // Cannot share with the owner
790
+            if ($share->getShareType() === IShare::TYPE_USER &&
791
+                $share->getSharedWith() === $share->getShareOwner()) {
792
+                throw new \InvalidArgumentException('Cannot share with the share owner');
793
+            }
794
+
795
+            // Generate the target
796
+            $defaultShareFolder = $this->config->getSystemValue('share_folder', '/');
797
+            $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
798
+            if ($allowCustomShareFolder) {
799
+                $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $defaultShareFolder);
800
+            } else {
801
+                $shareFolder = $defaultShareFolder;
802
+            }
803
+
804
+            $target = $shareFolder . '/' . $share->getNode()->getName();
805
+            $target = \OC\Files\Filesystem::normalizePath($target);
806
+            $share->setTarget($target);
807
+
808
+            // Pre share event
809
+            $event = new GenericEvent($share);
810
+            $this->legacyDispatcher->dispatch('OCP\Share::preShare', $event);
811
+            if ($event->isPropagationStopped() && $event->hasArgument('error')) {
812
+                throw new \Exception($event->getArgument('error'));
813
+            }
814
+
815
+            $oldShare = $share;
816
+            $provider = $this->factory->getProviderForType($share->getShareType());
817
+            $share = $provider->create($share);
818
+
819
+            // Reuse the node we already have
820
+            $share->setNode($oldShare->getNode());
821
+
822
+            // Reset the target if it is null for the new share
823
+            if ($share->getTarget() === '') {
824
+                $share->setTarget($target);
825
+            }
826
+        } catch (AlreadySharedException $e) {
827
+            // if a share for the same target already exists, dont create a new one, but do trigger the hooks and notifications again
828
+            $oldShare = $share;
829
+
830
+            // Reuse the node we already have
831
+            $share = $e->getExistingShare();
832
+            $share->setNode($oldShare->getNode());
833
+        }
834
+
835
+        // Post share event
836
+        $event = new GenericEvent($share);
837
+        $this->legacyDispatcher->dispatch('OCP\Share::postShare', $event);
838
+
839
+        $this->dispatcher->dispatchTyped(new Share\Events\ShareCreatedEvent($share));
840
+
841
+        if ($this->config->getSystemValueBool('sharing.enable_share_mail', true)
842
+            && $share->getShareType() === IShare::TYPE_USER) {
843
+            $mailSend = $share->getMailSend();
844
+            if ($mailSend === true) {
845
+                $user = $this->userManager->get($share->getSharedWith());
846
+                if ($user !== null) {
847
+                    $emailAddress = $user->getEMailAddress();
848
+                    if ($emailAddress !== null && $emailAddress !== '') {
849
+                        $userLang = $this->l10nFactory->getUserLanguage($user);
850
+                        $l = $this->l10nFactory->get('lib', $userLang);
851
+                        $this->sendMailNotification(
852
+                            $l,
853
+                            $share->getNode()->getName(),
854
+                            $this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]),
855
+                            $share->getSharedBy(),
856
+                            $emailAddress,
857
+                            $share->getExpirationDate(),
858
+                            $share->getNote()
859
+                        );
860
+                        $this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId(), ['app' => 'share']);
861
+                    } else {
862
+                        $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']);
863
+                    }
864
+                } else {
865
+                    $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']);
866
+                }
867
+            } else {
868
+                $this->logger->debug('Share notification not sent because mailsend is false.', ['app' => 'share']);
869
+            }
870
+        }
871
+
872
+        return $share;
873
+    }
874
+
875
+    /**
876
+     * Send mail notifications
877
+     *
878
+     * This method will catch and log mail transmission errors
879
+     *
880
+     * @param IL10N $l Language of the recipient
881
+     * @param string $filename file/folder name
882
+     * @param string $link link to the file/folder
883
+     * @param string $initiator user ID of share sender
884
+     * @param string $shareWith email address of share receiver
885
+     * @param \DateTime|null $expiration
886
+     */
887
+    protected function sendMailNotification(IL10N $l,
888
+                                            $filename,
889
+                                            $link,
890
+                                            $initiator,
891
+                                            $shareWith,
892
+                                            \DateTime $expiration = null,
893
+                                            $note = '') {
894
+        $initiatorUser = $this->userManager->get($initiator);
895
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
896
+
897
+        $message = $this->mailer->createMessage();
898
+
899
+        $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [
900
+            'filename' => $filename,
901
+            'link' => $link,
902
+            'initiator' => $initiatorDisplayName,
903
+            'expiration' => $expiration,
904
+            'shareWith' => $shareWith,
905
+        ]);
906
+
907
+        $emailTemplate->setSubject($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]));
908
+        $emailTemplate->addHeader();
909
+        $emailTemplate->addHeading($l->t('%1$s shared »%2$s« with you', [$initiatorDisplayName, $filename]), false);
910
+        $text = $l->t('%1$s shared »%2$s« with you.', [$initiatorDisplayName, $filename]);
911
+
912
+        if ($note !== '') {
913
+            $emailTemplate->addBodyText(htmlspecialchars($note), $note);
914
+        }
915
+
916
+        $emailTemplate->addBodyText(
917
+            htmlspecialchars($text . ' ' . $l->t('Click the button below to open it.')),
918
+            $text
919
+        );
920
+        $emailTemplate->addBodyButton(
921
+            $l->t('Open »%s«', [$filename]),
922
+            $link
923
+        );
924
+
925
+        $message->setTo([$shareWith]);
926
+
927
+        // The "From" contains the sharers name
928
+        $instanceName = $this->defaults->getName();
929
+        $senderName = $l->t(
930
+            '%1$s via %2$s',
931
+            [
932
+                $initiatorDisplayName,
933
+                $instanceName,
934
+            ]
935
+        );
936
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]);
937
+
938
+        // The "Reply-To" is set to the sharer if an mail address is configured
939
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
940
+        $initiatorEmail = $initiatorUser->getEMailAddress();
941
+        if ($initiatorEmail !== null) {
942
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
943
+            $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan($l->getLanguageCode()) !== '' ? ' - ' . $this->defaults->getSlogan($l->getLanguageCode()) : ''));
944
+        } else {
945
+            $emailTemplate->addFooter('', $l->getLanguageCode());
946
+        }
947
+
948
+        $message->useTemplate($emailTemplate);
949
+        try {
950
+            $failedRecipients = $this->mailer->send($message);
951
+            if (!empty($failedRecipients)) {
952
+                $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients));
953
+                return;
954
+            }
955
+        } catch (\Exception $e) {
956
+            $this->logger->error('Share notification mail could not be sent', ['exception' => $e]);
957
+        }
958
+    }
959
+
960
+    /**
961
+     * Update a share
962
+     *
963
+     * @param IShare $share
964
+     * @return IShare The share object
965
+     * @throws \InvalidArgumentException
966
+     */
967
+    public function updateShare(IShare $share) {
968
+        $expirationDateUpdated = false;
969
+
970
+        $this->canShare($share);
971
+
972
+        try {
973
+            $originalShare = $this->getShareById($share->getFullId());
974
+        } catch (\UnexpectedValueException $e) {
975
+            throw new \InvalidArgumentException('Share does not have a full id');
976
+        }
977
+
978
+        // We cannot change the share type!
979
+        if ($share->getShareType() !== $originalShare->getShareType()) {
980
+            throw new \InvalidArgumentException('Cannot change share type');
981
+        }
982
+
983
+        // We can only change the recipient on user shares
984
+        if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
985
+            $share->getShareType() !== IShare::TYPE_USER) {
986
+            throw new \InvalidArgumentException('Can only update recipient on user shares');
987
+        }
988
+
989
+        // Cannot share with the owner
990
+        if ($share->getShareType() === IShare::TYPE_USER &&
991
+            $share->getSharedWith() === $share->getShareOwner()) {
992
+            throw new \InvalidArgumentException('Cannot share with the share owner');
993
+        }
994
+
995
+        $this->generalCreateChecks($share);
996
+
997
+        if ($share->getShareType() === IShare::TYPE_USER) {
998
+            $this->userCreateChecks($share);
999
+
1000
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1001
+                //Verify the expiration date
1002
+                $this->validateExpirationDateInternal($share);
1003
+                $expirationDateUpdated = true;
1004
+            }
1005
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
1006
+            $this->groupCreateChecks($share);
1007
+
1008
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1009
+                //Verify the expiration date
1010
+                $this->validateExpirationDateInternal($share);
1011
+                $expirationDateUpdated = true;
1012
+            }
1013
+        } elseif ($share->getShareType() === IShare::TYPE_LINK
1014
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1015
+            $this->linkCreateChecks($share);
1016
+
1017
+            // The new password is not set again if it is the same as the old
1018
+            // one, unless when switching from sending by Talk to sending by
1019
+            // mail.
1020
+            $plainTextPassword = $share->getPassword();
1021
+            $updatedPassword = $this->updateSharePasswordIfNeeded($share, $originalShare);
1022
+
1023
+            /**
1024
+             * Cannot enable the getSendPasswordByTalk if there is no password set
1025
+             */
1026
+            if (empty($plainTextPassword) && $share->getSendPasswordByTalk()) {
1027
+                throw new \InvalidArgumentException('Cannot enable sending the password by Talk with an empty password');
1028
+            }
1029
+
1030
+            /**
1031
+             * If we're in a mail share, we need to force a password change
1032
+             * as either the user is not aware of the password or is already (received by mail)
1033
+             * Thus the SendPasswordByTalk feature would not make sense
1034
+             */
1035
+            if (!$updatedPassword && $share->getShareType() === IShare::TYPE_EMAIL) {
1036
+                if (!$originalShare->getSendPasswordByTalk() && $share->getSendPasswordByTalk()) {
1037
+                    throw new \InvalidArgumentException('Cannot enable sending the password by Talk without setting a new password');
1038
+                }
1039
+                if ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()) {
1040
+                    throw new \InvalidArgumentException('Cannot disable sending the password by Talk without setting a new password');
1041
+                }
1042
+            }
1043
+
1044
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1045
+                // Verify the expiration date
1046
+                $this->validateExpirationDateLink($share);
1047
+                $expirationDateUpdated = true;
1048
+            }
1049
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
1050
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
1051
+                //Verify the expiration date
1052
+                $this->validateExpirationDateInternal($share);
1053
+                $expirationDateUpdated = true;
1054
+            }
1055
+        }
1056
+
1057
+        $this->pathCreateChecks($share->getNode());
1058
+
1059
+        // Now update the share!
1060
+        $provider = $this->factory->getProviderForType($share->getShareType());
1061
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
1062
+            $share = $provider->update($share, $plainTextPassword);
1063
+        } else {
1064
+            $share = $provider->update($share);
1065
+        }
1066
+
1067
+        if ($expirationDateUpdated === true) {
1068
+            \OC_Hook::emit(Share::class, 'post_set_expiration_date', [
1069
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1070
+                'itemSource' => $share->getNode()->getId(),
1071
+                'date' => $share->getExpirationDate(),
1072
+                'uidOwner' => $share->getSharedBy(),
1073
+            ]);
1074
+        }
1075
+
1076
+        if ($share->getPassword() !== $originalShare->getPassword()) {
1077
+            \OC_Hook::emit(Share::class, 'post_update_password', [
1078
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1079
+                'itemSource' => $share->getNode()->getId(),
1080
+                'uidOwner' => $share->getSharedBy(),
1081
+                'token' => $share->getToken(),
1082
+                'disabled' => is_null($share->getPassword()),
1083
+            ]);
1084
+        }
1085
+
1086
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
1087
+            if ($this->userManager->userExists($share->getShareOwner())) {
1088
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
1089
+            } else {
1090
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1091
+            }
1092
+            \OC_Hook::emit(Share::class, 'post_update_permissions', [
1093
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
1094
+                'itemSource' => $share->getNode()->getId(),
1095
+                'shareType' => $share->getShareType(),
1096
+                'shareWith' => $share->getSharedWith(),
1097
+                'uidOwner' => $share->getSharedBy(),
1098
+                'permissions' => $share->getPermissions(),
1099
+                'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
1100
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
1101
+            ]);
1102
+        }
1103
+
1104
+        return $share;
1105
+    }
1106
+
1107
+    /**
1108
+     * Accept a share.
1109
+     *
1110
+     * @param IShare $share
1111
+     * @param string $recipientId
1112
+     * @return IShare The share object
1113
+     * @throws \InvalidArgumentException
1114
+     * @since 9.0.0
1115
+     */
1116
+    public function acceptShare(IShare $share, string $recipientId): IShare {
1117
+        [$providerId,] = $this->splitFullId($share->getFullId());
1118
+        $provider = $this->factory->getProvider($providerId);
1119
+
1120
+        if (!method_exists($provider, 'acceptShare')) {
1121
+            // TODO FIX ME
1122
+            throw new \InvalidArgumentException('Share provider does not support accepting');
1123
+        }
1124
+        $provider->acceptShare($share, $recipientId);
1125
+        $event = new GenericEvent($share);
1126
+        $this->legacyDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
1127
+
1128
+        return $share;
1129
+    }
1130
+
1131
+    /**
1132
+     * Updates the password of the given share if it is not the same as the
1133
+     * password of the original share.
1134
+     *
1135
+     * @param IShare $share the share to update its password.
1136
+     * @param IShare $originalShare the original share to compare its
1137
+     *        password with.
1138
+     * @return boolean whether the password was updated or not.
1139
+     */
1140
+    private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
1141
+        $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
1142
+            (($share->getPassword() !== null && $originalShare->getPassword() === null) ||
1143
+                ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
1144
+                ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
1145
+                    !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
1146
+
1147
+        // Password updated.
1148
+        if ($passwordsAreDifferent) {
1149
+            //Verify the password
1150
+            $this->verifyPassword($share->getPassword());
1151
+
1152
+            // If a password is set. Hash it!
1153
+            if (!empty($share->getPassword())) {
1154
+                $share->setPassword($this->hasher->hash($share->getPassword()));
1155
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
1156
+                    // Shares shared by email have temporary passwords
1157
+                    $this->setSharePasswordExpirationTime($share);
1158
+                }
1159
+
1160
+                return true;
1161
+            } else {
1162
+                // Empty string and null are seen as NOT password protected
1163
+                $share->setPassword(null);
1164
+                if ($share->getShareType() === IShare::TYPE_EMAIL) {
1165
+                    $share->setPasswordExpirationTime(null);
1166
+                }
1167
+                return true;
1168
+            }
1169
+        } else {
1170
+            // Reset the password to the original one, as it is either the same
1171
+            // as the "new" password or a hashed version of it.
1172
+            $share->setPassword($originalShare->getPassword());
1173
+        }
1174
+
1175
+        return false;
1176
+    }
1177
+
1178
+    /**
1179
+     * Set the share's password expiration time
1180
+     */
1181
+    private function setSharePasswordExpirationTime(IShare $share): void {
1182
+        if (!$this->config->getSystemValue('sharing.enable_mail_link_password_expiration', false)) {
1183
+            // Sets password expiration date to NULL
1184
+            $share->setPasswordExpirationTime();
1185
+            return;
1186
+        }
1187
+        // Sets password expiration date
1188
+        $expirationTime = null;
1189
+        $now = new \DateTime();
1190
+        $expirationInterval = $this->config->getSystemValue('sharing.mail_link_password_expiration_interval', 3600);
1191
+        $expirationTime = $now->add(new \DateInterval('PT' . $expirationInterval . 'S'));
1192
+        $share->setPasswordExpirationTime($expirationTime);
1193
+    }
1194
+
1195
+
1196
+    /**
1197
+     * Delete all the children of this share
1198
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
1199
+     *
1200
+     * @param IShare $share
1201
+     * @return IShare[] List of deleted shares
1202
+     */
1203
+    protected function deleteChildren(IShare $share) {
1204
+        $deletedShares = [];
1205
+
1206
+        $provider = $this->factory->getProviderForType($share->getShareType());
1207
+
1208
+        foreach ($provider->getChildren($share) as $child) {
1209
+            $deletedChildren = $this->deleteChildren($child);
1210
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
1211
+
1212
+            $provider->delete($child);
1213
+            $this->dispatcher->dispatchTyped(new Share\Events\ShareDeletedEvent($child));
1214
+            $deletedShares[] = $child;
1215
+        }
1216
+
1217
+        return $deletedShares;
1218
+    }
1219
+
1220
+    /**
1221
+     * Delete a share
1222
+     *
1223
+     * @param IShare $share
1224
+     * @throws ShareNotFound
1225
+     * @throws \InvalidArgumentException
1226
+     */
1227
+    public function deleteShare(IShare $share) {
1228
+        try {
1229
+            $share->getFullId();
1230
+        } catch (\UnexpectedValueException $e) {
1231
+            throw new \InvalidArgumentException('Share does not have a full id');
1232
+        }
1233
+
1234
+        $event = new GenericEvent($share);
1235
+        $this->legacyDispatcher->dispatch('OCP\Share::preUnshare', $event);
1236
+
1237
+        // Get all children and delete them as well
1238
+        $deletedShares = $this->deleteChildren($share);
1239
+
1240
+        // Do the actual delete
1241
+        $provider = $this->factory->getProviderForType($share->getShareType());
1242
+        $provider->delete($share);
1243
+
1244
+        $this->dispatcher->dispatchTyped(new Share\Events\ShareDeletedEvent($share));
1245
+
1246
+        // All the deleted shares caused by this delete
1247
+        $deletedShares[] = $share;
1248
+
1249
+        // Emit post hook
1250
+        $event->setArgument('deletedShares', $deletedShares);
1251
+        $this->legacyDispatcher->dispatch('OCP\Share::postUnshare', $event);
1252
+    }
1253
+
1254
+
1255
+    /**
1256
+     * Unshare a file as the recipient.
1257
+     * This can be different from a regular delete for example when one of
1258
+     * the users in a groups deletes that share. But the provider should
1259
+     * handle this.
1260
+     *
1261
+     * @param IShare $share
1262
+     * @param string $recipientId
1263
+     */
1264
+    public function deleteFromSelf(IShare $share, $recipientId) {
1265
+        [$providerId,] = $this->splitFullId($share->getFullId());
1266
+        $provider = $this->factory->getProvider($providerId);
1267
+
1268
+        $provider->deleteFromSelf($share, $recipientId);
1269
+        $event = new GenericEvent($share);
1270
+        $this->legacyDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event);
1271
+    }
1272
+
1273
+    public function restoreShare(IShare $share, string $recipientId): IShare {
1274
+        [$providerId,] = $this->splitFullId($share->getFullId());
1275
+        $provider = $this->factory->getProvider($providerId);
1276
+
1277
+        return $provider->restore($share, $recipientId);
1278
+    }
1279
+
1280
+    /**
1281
+     * @inheritdoc
1282
+     */
1283
+    public function moveShare(IShare $share, $recipientId) {
1284
+        if ($share->getShareType() === IShare::TYPE_LINK
1285
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
1286
+            throw new \InvalidArgumentException('Cannot change target of link share');
1287
+        }
1288
+
1289
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() !== $recipientId) {
1290
+            throw new \InvalidArgumentException('Invalid recipient');
1291
+        }
1292
+
1293
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1294
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1295
+            if (is_null($sharedWith)) {
1296
+                throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
1297
+            }
1298
+            $recipient = $this->userManager->get($recipientId);
1299
+            if (!$sharedWith->inGroup($recipient)) {
1300
+                throw new \InvalidArgumentException('Invalid recipient');
1301
+            }
1302
+        }
1303
+
1304
+        [$providerId,] = $this->splitFullId($share->getFullId());
1305
+        $provider = $this->factory->getProvider($providerId);
1306
+
1307
+        return $provider->move($share, $recipientId);
1308
+    }
1309
+
1310
+    public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
1311
+        $providers = $this->factory->getAllProviders();
1312
+
1313
+        return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) {
1314
+            $newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow);
1315
+            foreach ($newShares as $fid => $data) {
1316
+                if (!isset($shares[$fid])) {
1317
+                    $shares[$fid] = [];
1318
+                }
1319
+
1320
+                $shares[$fid] = array_merge($shares[$fid], $data);
1321
+            }
1322
+            return $shares;
1323
+        }, []);
1324
+    }
1325
+
1326
+    /**
1327
+     * @inheritdoc
1328
+     */
1329
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
1330
+        if ($path !== null &&
1331
+            !($path instanceof \OCP\Files\File) &&
1332
+            !($path instanceof \OCP\Files\Folder)) {
1333
+            throw new \InvalidArgumentException('invalid path');
1334
+        }
1335
+
1336
+        try {
1337
+            $provider = $this->factory->getProviderForType($shareType);
1338
+        } catch (ProviderException $e) {
1339
+            return [];
1340
+        }
1341
+
1342
+        $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1343
+
1344
+        /*
1345 1345
 		 * Work around so we don't return expired shares but still follow
1346 1346
 		 * proper pagination.
1347 1347
 		 */
1348 1348
 
1349
-		$shares2 = [];
1350
-
1351
-		while (true) {
1352
-			$added = 0;
1353
-			foreach ($shares as $share) {
1354
-				try {
1355
-					$this->checkExpireDate($share);
1356
-				} catch (ShareNotFound $e) {
1357
-					//Ignore since this basically means the share is deleted
1358
-					continue;
1359
-				}
1360
-
1361
-				$added++;
1362
-				$shares2[] = $share;
1363
-
1364
-				if (count($shares2) === $limit) {
1365
-					break;
1366
-				}
1367
-			}
1368
-
1369
-			// If we did not fetch more shares than the limit then there are no more shares
1370
-			if (count($shares) < $limit) {
1371
-				break;
1372
-			}
1373
-
1374
-			if (count($shares2) === $limit) {
1375
-				break;
1376
-			}
1377
-
1378
-			// If there was no limit on the select we are done
1379
-			if ($limit === -1) {
1380
-				break;
1381
-			}
1382
-
1383
-			$offset += $added;
1384
-
1385
-			// Fetch again $limit shares
1386
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1387
-
1388
-			// No more shares means we are done
1389
-			if (empty($shares)) {
1390
-				break;
1391
-			}
1392
-		}
1393
-
1394
-		$shares = $shares2;
1395
-
1396
-		return $shares;
1397
-	}
1398
-
1399
-	/**
1400
-	 * @inheritdoc
1401
-	 */
1402
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1403
-		try {
1404
-			$provider = $this->factory->getProviderForType($shareType);
1405
-		} catch (ProviderException $e) {
1406
-			return [];
1407
-		}
1408
-
1409
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1410
-
1411
-		// remove all shares which are already expired
1412
-		foreach ($shares as $key => $share) {
1413
-			try {
1414
-				$this->checkExpireDate($share);
1415
-			} catch (ShareNotFound $e) {
1416
-				unset($shares[$key]);
1417
-			}
1418
-		}
1419
-
1420
-		return $shares;
1421
-	}
1422
-
1423
-	/**
1424
-	 * @inheritdoc
1425
-	 */
1426
-	public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1427
-		$shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1428
-
1429
-		// Only get deleted shares
1430
-		$shares = array_filter($shares, function (IShare $share) {
1431
-			return $share->getPermissions() === 0;
1432
-		});
1433
-
1434
-		// Only get shares where the owner still exists
1435
-		$shares = array_filter($shares, function (IShare $share) {
1436
-			return $this->userManager->userExists($share->getShareOwner());
1437
-		});
1438
-
1439
-		return $shares;
1440
-	}
1441
-
1442
-	/**
1443
-	 * @inheritdoc
1444
-	 */
1445
-	public function getShareById($id, $recipient = null) {
1446
-		if ($id === null) {
1447
-			throw new ShareNotFound();
1448
-		}
1449
-
1450
-		[$providerId, $id] = $this->splitFullId($id);
1451
-
1452
-		try {
1453
-			$provider = $this->factory->getProvider($providerId);
1454
-		} catch (ProviderException $e) {
1455
-			throw new ShareNotFound();
1456
-		}
1457
-
1458
-		$share = $provider->getShareById($id, $recipient);
1459
-
1460
-		$this->checkExpireDate($share);
1461
-
1462
-		return $share;
1463
-	}
1464
-
1465
-	/**
1466
-	 * Get all the shares for a given path
1467
-	 *
1468
-	 * @param \OCP\Files\Node $path
1469
-	 * @param int $page
1470
-	 * @param int $perPage
1471
-	 *
1472
-	 * @return Share[]
1473
-	 */
1474
-	public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1475
-		return [];
1476
-	}
1477
-
1478
-	/**
1479
-	 * Get the share by token possible with password
1480
-	 *
1481
-	 * @param string $token
1482
-	 * @return IShare
1483
-	 *
1484
-	 * @throws ShareNotFound
1485
-	 */
1486
-	public function getShareByToken($token) {
1487
-		// tokens cannot be valid local user names
1488
-		if ($this->userManager->userExists($token)) {
1489
-			throw new ShareNotFound();
1490
-		}
1491
-		$share = null;
1492
-		try {
1493
-			if ($this->shareApiAllowLinks()) {
1494
-				$provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1495
-				$share = $provider->getShareByToken($token);
1496
-			}
1497
-		} catch (ProviderException $e) {
1498
-		} catch (ShareNotFound $e) {
1499
-		}
1500
-
1501
-
1502
-		// If it is not a link share try to fetch a federated share by token
1503
-		if ($share === null) {
1504
-			try {
1505
-				$provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1506
-				$share = $provider->getShareByToken($token);
1507
-			} catch (ProviderException $e) {
1508
-			} catch (ShareNotFound $e) {
1509
-			}
1510
-		}
1511
-
1512
-		// If it is not a link share try to fetch a mail share by token
1513
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1514
-			try {
1515
-				$provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1516
-				$share = $provider->getShareByToken($token);
1517
-			} catch (ProviderException $e) {
1518
-			} catch (ShareNotFound $e) {
1519
-			}
1520
-		}
1521
-
1522
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1523
-			try {
1524
-				$provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1525
-				$share = $provider->getShareByToken($token);
1526
-			} catch (ProviderException $e) {
1527
-			} catch (ShareNotFound $e) {
1528
-			}
1529
-		}
1530
-
1531
-		if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1532
-			try {
1533
-				$provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1534
-				$share = $provider->getShareByToken($token);
1535
-			} catch (ProviderException $e) {
1536
-			} catch (ShareNotFound $e) {
1537
-			}
1538
-		}
1539
-
1540
-		if ($share === null) {
1541
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1542
-		}
1543
-
1544
-		$this->checkExpireDate($share);
1545
-
1546
-		/*
1349
+        $shares2 = [];
1350
+
1351
+        while (true) {
1352
+            $added = 0;
1353
+            foreach ($shares as $share) {
1354
+                try {
1355
+                    $this->checkExpireDate($share);
1356
+                } catch (ShareNotFound $e) {
1357
+                    //Ignore since this basically means the share is deleted
1358
+                    continue;
1359
+                }
1360
+
1361
+                $added++;
1362
+                $shares2[] = $share;
1363
+
1364
+                if (count($shares2) === $limit) {
1365
+                    break;
1366
+                }
1367
+            }
1368
+
1369
+            // If we did not fetch more shares than the limit then there are no more shares
1370
+            if (count($shares) < $limit) {
1371
+                break;
1372
+            }
1373
+
1374
+            if (count($shares2) === $limit) {
1375
+                break;
1376
+            }
1377
+
1378
+            // If there was no limit on the select we are done
1379
+            if ($limit === -1) {
1380
+                break;
1381
+            }
1382
+
1383
+            $offset += $added;
1384
+
1385
+            // Fetch again $limit shares
1386
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
1387
+
1388
+            // No more shares means we are done
1389
+            if (empty($shares)) {
1390
+                break;
1391
+            }
1392
+        }
1393
+
1394
+        $shares = $shares2;
1395
+
1396
+        return $shares;
1397
+    }
1398
+
1399
+    /**
1400
+     * @inheritdoc
1401
+     */
1402
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1403
+        try {
1404
+            $provider = $this->factory->getProviderForType($shareType);
1405
+        } catch (ProviderException $e) {
1406
+            return [];
1407
+        }
1408
+
1409
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1410
+
1411
+        // remove all shares which are already expired
1412
+        foreach ($shares as $key => $share) {
1413
+            try {
1414
+                $this->checkExpireDate($share);
1415
+            } catch (ShareNotFound $e) {
1416
+                unset($shares[$key]);
1417
+            }
1418
+        }
1419
+
1420
+        return $shares;
1421
+    }
1422
+
1423
+    /**
1424
+     * @inheritdoc
1425
+     */
1426
+    public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
1427
+        $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset);
1428
+
1429
+        // Only get deleted shares
1430
+        $shares = array_filter($shares, function (IShare $share) {
1431
+            return $share->getPermissions() === 0;
1432
+        });
1433
+
1434
+        // Only get shares where the owner still exists
1435
+        $shares = array_filter($shares, function (IShare $share) {
1436
+            return $this->userManager->userExists($share->getShareOwner());
1437
+        });
1438
+
1439
+        return $shares;
1440
+    }
1441
+
1442
+    /**
1443
+     * @inheritdoc
1444
+     */
1445
+    public function getShareById($id, $recipient = null) {
1446
+        if ($id === null) {
1447
+            throw new ShareNotFound();
1448
+        }
1449
+
1450
+        [$providerId, $id] = $this->splitFullId($id);
1451
+
1452
+        try {
1453
+            $provider = $this->factory->getProvider($providerId);
1454
+        } catch (ProviderException $e) {
1455
+            throw new ShareNotFound();
1456
+        }
1457
+
1458
+        $share = $provider->getShareById($id, $recipient);
1459
+
1460
+        $this->checkExpireDate($share);
1461
+
1462
+        return $share;
1463
+    }
1464
+
1465
+    /**
1466
+     * Get all the shares for a given path
1467
+     *
1468
+     * @param \OCP\Files\Node $path
1469
+     * @param int $page
1470
+     * @param int $perPage
1471
+     *
1472
+     * @return Share[]
1473
+     */
1474
+    public function getSharesByPath(\OCP\Files\Node $path, $page = 0, $perPage = 50) {
1475
+        return [];
1476
+    }
1477
+
1478
+    /**
1479
+     * Get the share by token possible with password
1480
+     *
1481
+     * @param string $token
1482
+     * @return IShare
1483
+     *
1484
+     * @throws ShareNotFound
1485
+     */
1486
+    public function getShareByToken($token) {
1487
+        // tokens cannot be valid local user names
1488
+        if ($this->userManager->userExists($token)) {
1489
+            throw new ShareNotFound();
1490
+        }
1491
+        $share = null;
1492
+        try {
1493
+            if ($this->shareApiAllowLinks()) {
1494
+                $provider = $this->factory->getProviderForType(IShare::TYPE_LINK);
1495
+                $share = $provider->getShareByToken($token);
1496
+            }
1497
+        } catch (ProviderException $e) {
1498
+        } catch (ShareNotFound $e) {
1499
+        }
1500
+
1501
+
1502
+        // If it is not a link share try to fetch a federated share by token
1503
+        if ($share === null) {
1504
+            try {
1505
+                $provider = $this->factory->getProviderForType(IShare::TYPE_REMOTE);
1506
+                $share = $provider->getShareByToken($token);
1507
+            } catch (ProviderException $e) {
1508
+            } catch (ShareNotFound $e) {
1509
+            }
1510
+        }
1511
+
1512
+        // If it is not a link share try to fetch a mail share by token
1513
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_EMAIL)) {
1514
+            try {
1515
+                $provider = $this->factory->getProviderForType(IShare::TYPE_EMAIL);
1516
+                $share = $provider->getShareByToken($token);
1517
+            } catch (ProviderException $e) {
1518
+            } catch (ShareNotFound $e) {
1519
+            }
1520
+        }
1521
+
1522
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_CIRCLE)) {
1523
+            try {
1524
+                $provider = $this->factory->getProviderForType(IShare::TYPE_CIRCLE);
1525
+                $share = $provider->getShareByToken($token);
1526
+            } catch (ProviderException $e) {
1527
+            } catch (ShareNotFound $e) {
1528
+            }
1529
+        }
1530
+
1531
+        if ($share === null && $this->shareProviderExists(IShare::TYPE_ROOM)) {
1532
+            try {
1533
+                $provider = $this->factory->getProviderForType(IShare::TYPE_ROOM);
1534
+                $share = $provider->getShareByToken($token);
1535
+            } catch (ProviderException $e) {
1536
+            } catch (ShareNotFound $e) {
1537
+            }
1538
+        }
1539
+
1540
+        if ($share === null) {
1541
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1542
+        }
1543
+
1544
+        $this->checkExpireDate($share);
1545
+
1546
+        /*
1547 1547
 		 * Reduce the permissions for link or email shares if public upload is not enabled
1548 1548
 		 */
1549
-		if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1550
-			&& $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1551
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1552
-		}
1553
-
1554
-		return $share;
1555
-	}
1556
-
1557
-	protected function checkExpireDate($share) {
1558
-		if ($share->isExpired()) {
1559
-			$this->deleteShare($share);
1560
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1561
-		}
1562
-	}
1563
-
1564
-	/**
1565
-	 * Verify the password of a public share
1566
-	 *
1567
-	 * @param IShare $share
1568
-	 * @param ?string $password
1569
-	 * @return bool
1570
-	 */
1571
-	public function checkPassword(IShare $share, $password) {
1572
-		$passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
1573
-			|| $share->getShareType() !== IShare::TYPE_EMAIL
1574
-			|| $share->getShareType() !== IShare::TYPE_CIRCLE;
1575
-		if (!$passwordProtected) {
1576
-			//TODO maybe exception?
1577
-			return false;
1578
-		}
1579
-
1580
-		if ($password === null || $share->getPassword() === null) {
1581
-			return false;
1582
-		}
1583
-
1584
-		// Makes sure password hasn't expired
1585
-		$expirationTime = $share->getPasswordExpirationTime();
1586
-		if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1587
-			return false;
1588
-		}
1589
-
1590
-		$newHash = '';
1591
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1592
-			return false;
1593
-		}
1594
-
1595
-		if (!empty($newHash)) {
1596
-			$share->setPassword($newHash);
1597
-			$provider = $this->factory->getProviderForType($share->getShareType());
1598
-			$provider->update($share);
1599
-		}
1600
-
1601
-		return true;
1602
-	}
1603
-
1604
-	/**
1605
-	 * @inheritdoc
1606
-	 */
1607
-	public function userDeleted($uid) {
1608
-		$types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1609
-
1610
-		foreach ($types as $type) {
1611
-			try {
1612
-				$provider = $this->factory->getProviderForType($type);
1613
-			} catch (ProviderException $e) {
1614
-				continue;
1615
-			}
1616
-			$provider->userDeleted($uid, $type);
1617
-		}
1618
-	}
1619
-
1620
-	/**
1621
-	 * @inheritdoc
1622
-	 */
1623
-	public function groupDeleted($gid) {
1624
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1625
-		$provider->groupDeleted($gid);
1626
-
1627
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1628
-		if ($excludedGroups === '') {
1629
-			return;
1630
-		}
1631
-
1632
-		$excludedGroups = json_decode($excludedGroups, true);
1633
-		if (json_last_error() !== JSON_ERROR_NONE) {
1634
-			return;
1635
-		}
1636
-
1637
-		$excludedGroups = array_diff($excludedGroups, [$gid]);
1638
-		$this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1639
-	}
1640
-
1641
-	/**
1642
-	 * @inheritdoc
1643
-	 */
1644
-	public function userDeletedFromGroup($uid, $gid) {
1645
-		$provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1646
-		$provider->userDeletedFromGroup($uid, $gid);
1647
-	}
1648
-
1649
-	/**
1650
-	 * Get access list to a path. This means
1651
-	 * all the users that can access a given path.
1652
-	 *
1653
-	 * Consider:
1654
-	 * -root
1655
-	 * |-folder1 (23)
1656
-	 *  |-folder2 (32)
1657
-	 *   |-fileA (42)
1658
-	 *
1659
-	 * fileA is shared with user1 and user1@server1
1660
-	 * folder2 is shared with group2 (user4 is a member of group2)
1661
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1662
-	 *
1663
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1664
-	 * [
1665
-	 *  users  => [
1666
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1667
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1668
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1669
-	 *  ],
1670
-	 *  remote => [
1671
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1672
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1673
-	 *  ],
1674
-	 *  public => bool
1675
-	 *  mail => bool
1676
-	 * ]
1677
-	 *
1678
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1679
-	 * [
1680
-	 *  users  => ['user1', 'user2', 'user4'],
1681
-	 *  remote => bool,
1682
-	 *  public => bool
1683
-	 *  mail => bool
1684
-	 * ]
1685
-	 *
1686
-	 * This is required for encryption/activity
1687
-	 *
1688
-	 * @param \OCP\Files\Node $path
1689
-	 * @param bool $recursive Should we check all parent folders as well
1690
-	 * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1691
-	 * @return array
1692
-	 */
1693
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1694
-		$owner = $path->getOwner();
1695
-
1696
-		if ($owner === null) {
1697
-			return [];
1698
-		}
1699
-
1700
-		$owner = $owner->getUID();
1701
-
1702
-		if ($currentAccess) {
1703
-			$al = ['users' => [], 'remote' => [], 'public' => false];
1704
-		} else {
1705
-			$al = ['users' => [], 'remote' => false, 'public' => false];
1706
-		}
1707
-		if (!$this->userManager->userExists($owner)) {
1708
-			return $al;
1709
-		}
1710
-
1711
-		//Get node for the owner and correct the owner in case of external storage
1712
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1713
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1714
-			$nodes = $userFolder->getById($path->getId());
1715
-			$path = array_shift($nodes);
1716
-			if ($path === null || $path->getOwner() === null) {
1717
-				return [];
1718
-			}
1719
-			$owner = $path->getOwner()->getUID();
1720
-		}
1721
-
1722
-		$providers = $this->factory->getAllProviders();
1723
-
1724
-		/** @var Node[] $nodes */
1725
-		$nodes = [];
1726
-
1727
-
1728
-		if ($currentAccess) {
1729
-			$ownerPath = $path->getPath();
1730
-			$ownerPath = explode('/', $ownerPath, 4);
1731
-			if (count($ownerPath) < 4) {
1732
-				$ownerPath = '';
1733
-			} else {
1734
-				$ownerPath = $ownerPath[3];
1735
-			}
1736
-			$al['users'][$owner] = [
1737
-				'node_id' => $path->getId(),
1738
-				'node_path' => '/' . $ownerPath,
1739
-			];
1740
-		} else {
1741
-			$al['users'][] = $owner;
1742
-		}
1743
-
1744
-		// Collect all the shares
1745
-		while ($path->getPath() !== $userFolder->getPath()) {
1746
-			$nodes[] = $path;
1747
-			if (!$recursive) {
1748
-				break;
1749
-			}
1750
-			$path = $path->getParent();
1751
-		}
1752
-
1753
-		foreach ($providers as $provider) {
1754
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1755
-
1756
-			foreach ($tmp as $k => $v) {
1757
-				if (isset($al[$k])) {
1758
-					if (is_array($al[$k])) {
1759
-						if ($currentAccess) {
1760
-							$al[$k] += $v;
1761
-						} else {
1762
-							$al[$k] = array_merge($al[$k], $v);
1763
-							$al[$k] = array_unique($al[$k]);
1764
-							$al[$k] = array_values($al[$k]);
1765
-						}
1766
-					} else {
1767
-						$al[$k] = $al[$k] || $v;
1768
-					}
1769
-				} else {
1770
-					$al[$k] = $v;
1771
-				}
1772
-			}
1773
-		}
1774
-
1775
-		return $al;
1776
-	}
1777
-
1778
-	/**
1779
-	 * Create a new share
1780
-	 *
1781
-	 * @return IShare
1782
-	 */
1783
-	public function newShare() {
1784
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1785
-	}
1786
-
1787
-	/**
1788
-	 * Is the share API enabled
1789
-	 *
1790
-	 * @return bool
1791
-	 */
1792
-	public function shareApiEnabled() {
1793
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1794
-	}
1795
-
1796
-	/**
1797
-	 * Is public link sharing enabled
1798
-	 *
1799
-	 * @return bool
1800
-	 */
1801
-	public function shareApiAllowLinks() {
1802
-		if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1803
-			return false;
1804
-		}
1805
-
1806
-		$user = $this->userSession->getUser();
1807
-		if ($user) {
1808
-			$excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1809
-			if ($excludedGroups) {
1810
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1811
-				return !(bool)array_intersect($excludedGroups, $userGroups);
1812
-			}
1813
-		}
1814
-
1815
-		return true;
1816
-	}
1817
-
1818
-	/**
1819
-	 * Is password on public link requires
1820
-	 *
1821
-	 * @param bool Check group membership exclusion
1822
-	 * @return bool
1823
-	 */
1824
-	public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1825
-		$excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1826
-		if ($excludedGroups !== '' && $checkGroupMembership) {
1827
-			$excludedGroups = json_decode($excludedGroups);
1828
-			$user = $this->userSession->getUser();
1829
-			if ($user) {
1830
-				$userGroups = $this->groupManager->getUserGroupIds($user);
1831
-				if ((bool)array_intersect($excludedGroups, $userGroups)) {
1832
-					return false;
1833
-				}
1834
-			}
1835
-		}
1836
-		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1837
-	}
1838
-
1839
-	/**
1840
-	 * Is default link expire date enabled
1841
-	 *
1842
-	 * @return bool
1843
-	 */
1844
-	public function shareApiLinkDefaultExpireDate() {
1845
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1846
-	}
1847
-
1848
-	/**
1849
-	 * Is default link expire date enforced
1850
-	 *`
1851
-	 *
1852
-	 * @return bool
1853
-	 */
1854
-	public function shareApiLinkDefaultExpireDateEnforced() {
1855
-		return $this->shareApiLinkDefaultExpireDate() &&
1856
-			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1857
-	}
1858
-
1859
-
1860
-	/**
1861
-	 * Number of default link expire days
1862
-	 *
1863
-	 * @return int
1864
-	 */
1865
-	public function shareApiLinkDefaultExpireDays() {
1866
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1867
-	}
1868
-
1869
-	/**
1870
-	 * Is default internal expire date enabled
1871
-	 *
1872
-	 * @return bool
1873
-	 */
1874
-	public function shareApiInternalDefaultExpireDate(): bool {
1875
-		return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1876
-	}
1877
-
1878
-	/**
1879
-	 * Is default remote expire date enabled
1880
-	 *
1881
-	 * @return bool
1882
-	 */
1883
-	public function shareApiRemoteDefaultExpireDate(): bool {
1884
-		return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1885
-	}
1886
-
1887
-	/**
1888
-	 * Is default expire date enforced
1889
-	 *
1890
-	 * @return bool
1891
-	 */
1892
-	public function shareApiInternalDefaultExpireDateEnforced(): bool {
1893
-		return $this->shareApiInternalDefaultExpireDate() &&
1894
-			$this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1895
-	}
1896
-
1897
-	/**
1898
-	 * Is default expire date enforced for remote shares
1899
-	 *
1900
-	 * @return bool
1901
-	 */
1902
-	public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1903
-		return $this->shareApiRemoteDefaultExpireDate() &&
1904
-			$this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1905
-	}
1906
-
1907
-	/**
1908
-	 * Number of default expire days
1909
-	 *
1910
-	 * @return int
1911
-	 */
1912
-	public function shareApiInternalDefaultExpireDays(): int {
1913
-		return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1914
-	}
1915
-
1916
-	/**
1917
-	 * Number of default expire days for remote shares
1918
-	 *
1919
-	 * @return int
1920
-	 */
1921
-	public function shareApiRemoteDefaultExpireDays(): int {
1922
-		return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1923
-	}
1924
-
1925
-	/**
1926
-	 * Allow public upload on link shares
1927
-	 *
1928
-	 * @return bool
1929
-	 */
1930
-	public function shareApiLinkAllowPublicUpload() {
1931
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1932
-	}
1933
-
1934
-	/**
1935
-	 * check if user can only share with group members
1936
-	 *
1937
-	 * @return bool
1938
-	 */
1939
-	public function shareWithGroupMembersOnly() {
1940
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1941
-	}
1942
-
1943
-	/**
1944
-	 * Check if users can share with groups
1945
-	 *
1946
-	 * @return bool
1947
-	 */
1948
-	public function allowGroupSharing() {
1949
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1950
-	}
1951
-
1952
-	public function allowEnumeration(): bool {
1953
-		return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1954
-	}
1955
-
1956
-	public function limitEnumerationToGroups(): bool {
1957
-		return $this->allowEnumeration() &&
1958
-			$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1959
-	}
1960
-
1961
-	public function limitEnumerationToPhone(): bool {
1962
-		return $this->allowEnumeration() &&
1963
-			$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1964
-	}
1965
-
1966
-	public function allowEnumerationFullMatch(): bool {
1967
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1968
-	}
1969
-
1970
-	public function matchEmail(): bool {
1971
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1972
-	}
1973
-
1974
-	public function ignoreSecondDisplayName(): bool {
1975
-		return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1976
-	}
1977
-
1978
-	public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1979
-		if ($this->allowEnumerationFullMatch()) {
1980
-			return true;
1981
-		}
1982
-
1983
-		if (!$this->allowEnumeration()) {
1984
-			return false;
1985
-		}
1986
-
1987
-		if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1988
-			// Enumeration is enabled and not restricted: OK
1989
-			return true;
1990
-		}
1991
-
1992
-		if (!$currentUser instanceof IUser) {
1993
-			// Enumeration restrictions require an account
1994
-			return false;
1995
-		}
1996
-
1997
-		// Enumeration is limited to phone match
1998
-		if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1999
-			return true;
2000
-		}
2001
-
2002
-		// Enumeration is limited to groups
2003
-		if ($this->limitEnumerationToGroups()) {
2004
-			$currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
2005
-			$targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
2006
-			if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
2007
-				return true;
2008
-			}
2009
-		}
2010
-
2011
-		return false;
2012
-	}
2013
-
2014
-	/**
2015
-	 * Copied from \OC_Util::isSharingDisabledForUser
2016
-	 *
2017
-	 * TODO: Deprecate function from OC_Util
2018
-	 *
2019
-	 * @param string $userId
2020
-	 * @return bool
2021
-	 */
2022
-	public function sharingDisabledForUser($userId) {
2023
-		if ($userId === null) {
2024
-			return false;
2025
-		}
2026
-
2027
-		if (isset($this->sharingDisabledForUsersCache[$userId])) {
2028
-			return $this->sharingDisabledForUsersCache[$userId];
2029
-		}
2030
-
2031
-		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
2032
-			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
2033
-			$excludedGroups = json_decode($groupsList);
2034
-			if (is_null($excludedGroups)) {
2035
-				$excludedGroups = explode(',', $groupsList);
2036
-				$newValue = json_encode($excludedGroups);
2037
-				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
2038
-			}
2039
-			$user = $this->userManager->get($userId);
2040
-			$usersGroups = $this->groupManager->getUserGroupIds($user);
2041
-			if (!empty($usersGroups)) {
2042
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
2043
-				// if the user is only in groups which are disabled for sharing then
2044
-				// sharing is also disabled for the user
2045
-				if (empty($remainingGroups)) {
2046
-					$this->sharingDisabledForUsersCache[$userId] = true;
2047
-					return true;
2048
-				}
2049
-			}
2050
-		}
2051
-
2052
-		$this->sharingDisabledForUsersCache[$userId] = false;
2053
-		return false;
2054
-	}
2055
-
2056
-	/**
2057
-	 * @inheritdoc
2058
-	 */
2059
-	public function outgoingServer2ServerSharesAllowed() {
2060
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
2061
-	}
2062
-
2063
-	/**
2064
-	 * @inheritdoc
2065
-	 */
2066
-	public function outgoingServer2ServerGroupSharesAllowed() {
2067
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2068
-	}
2069
-
2070
-	/**
2071
-	 * @inheritdoc
2072
-	 */
2073
-	public function shareProviderExists($shareType) {
2074
-		try {
2075
-			$this->factory->getProviderForType($shareType);
2076
-		} catch (ProviderException $e) {
2077
-			return false;
2078
-		}
2079
-
2080
-		return true;
2081
-	}
2082
-
2083
-	public function registerShareProvider(string $shareProviderClass): void {
2084
-		$this->factory->registerProvider($shareProviderClass);
2085
-	}
2086
-
2087
-	public function getAllShares(): iterable {
2088
-		$providers = $this->factory->getAllProviders();
2089
-
2090
-		foreach ($providers as $provider) {
2091
-			yield from $provider->getAllShares();
2092
-		}
2093
-	}
1549
+        if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
1550
+            && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
1551
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1552
+        }
1553
+
1554
+        return $share;
1555
+    }
1556
+
1557
+    protected function checkExpireDate($share) {
1558
+        if ($share->isExpired()) {
1559
+            $this->deleteShare($share);
1560
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1561
+        }
1562
+    }
1563
+
1564
+    /**
1565
+     * Verify the password of a public share
1566
+     *
1567
+     * @param IShare $share
1568
+     * @param ?string $password
1569
+     * @return bool
1570
+     */
1571
+    public function checkPassword(IShare $share, $password) {
1572
+        $passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
1573
+            || $share->getShareType() !== IShare::TYPE_EMAIL
1574
+            || $share->getShareType() !== IShare::TYPE_CIRCLE;
1575
+        if (!$passwordProtected) {
1576
+            //TODO maybe exception?
1577
+            return false;
1578
+        }
1579
+
1580
+        if ($password === null || $share->getPassword() === null) {
1581
+            return false;
1582
+        }
1583
+
1584
+        // Makes sure password hasn't expired
1585
+        $expirationTime = $share->getPasswordExpirationTime();
1586
+        if ($expirationTime !== null && $expirationTime < new \DateTime()) {
1587
+            return false;
1588
+        }
1589
+
1590
+        $newHash = '';
1591
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1592
+            return false;
1593
+        }
1594
+
1595
+        if (!empty($newHash)) {
1596
+            $share->setPassword($newHash);
1597
+            $provider = $this->factory->getProviderForType($share->getShareType());
1598
+            $provider->update($share);
1599
+        }
1600
+
1601
+        return true;
1602
+    }
1603
+
1604
+    /**
1605
+     * @inheritdoc
1606
+     */
1607
+    public function userDeleted($uid) {
1608
+        $types = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL];
1609
+
1610
+        foreach ($types as $type) {
1611
+            try {
1612
+                $provider = $this->factory->getProviderForType($type);
1613
+            } catch (ProviderException $e) {
1614
+                continue;
1615
+            }
1616
+            $provider->userDeleted($uid, $type);
1617
+        }
1618
+    }
1619
+
1620
+    /**
1621
+     * @inheritdoc
1622
+     */
1623
+    public function groupDeleted($gid) {
1624
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1625
+        $provider->groupDeleted($gid);
1626
+
1627
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1628
+        if ($excludedGroups === '') {
1629
+            return;
1630
+        }
1631
+
1632
+        $excludedGroups = json_decode($excludedGroups, true);
1633
+        if (json_last_error() !== JSON_ERROR_NONE) {
1634
+            return;
1635
+        }
1636
+
1637
+        $excludedGroups = array_diff($excludedGroups, [$gid]);
1638
+        $this->config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode($excludedGroups));
1639
+    }
1640
+
1641
+    /**
1642
+     * @inheritdoc
1643
+     */
1644
+    public function userDeletedFromGroup($uid, $gid) {
1645
+        $provider = $this->factory->getProviderForType(IShare::TYPE_GROUP);
1646
+        $provider->userDeletedFromGroup($uid, $gid);
1647
+    }
1648
+
1649
+    /**
1650
+     * Get access list to a path. This means
1651
+     * all the users that can access a given path.
1652
+     *
1653
+     * Consider:
1654
+     * -root
1655
+     * |-folder1 (23)
1656
+     *  |-folder2 (32)
1657
+     *   |-fileA (42)
1658
+     *
1659
+     * fileA is shared with user1 and user1@server1
1660
+     * folder2 is shared with group2 (user4 is a member of group2)
1661
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1662
+     *
1663
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1664
+     * [
1665
+     *  users  => [
1666
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1667
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1668
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1669
+     *  ],
1670
+     *  remote => [
1671
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1672
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1673
+     *  ],
1674
+     *  public => bool
1675
+     *  mail => bool
1676
+     * ]
1677
+     *
1678
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1679
+     * [
1680
+     *  users  => ['user1', 'user2', 'user4'],
1681
+     *  remote => bool,
1682
+     *  public => bool
1683
+     *  mail => bool
1684
+     * ]
1685
+     *
1686
+     * This is required for encryption/activity
1687
+     *
1688
+     * @param \OCP\Files\Node $path
1689
+     * @param bool $recursive Should we check all parent folders as well
1690
+     * @param bool $currentAccess Ensure the recipient has access to the file (e.g. did not unshare it)
1691
+     * @return array
1692
+     */
1693
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1694
+        $owner = $path->getOwner();
1695
+
1696
+        if ($owner === null) {
1697
+            return [];
1698
+        }
1699
+
1700
+        $owner = $owner->getUID();
1701
+
1702
+        if ($currentAccess) {
1703
+            $al = ['users' => [], 'remote' => [], 'public' => false];
1704
+        } else {
1705
+            $al = ['users' => [], 'remote' => false, 'public' => false];
1706
+        }
1707
+        if (!$this->userManager->userExists($owner)) {
1708
+            return $al;
1709
+        }
1710
+
1711
+        //Get node for the owner and correct the owner in case of external storage
1712
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1713
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1714
+            $nodes = $userFolder->getById($path->getId());
1715
+            $path = array_shift($nodes);
1716
+            if ($path === null || $path->getOwner() === null) {
1717
+                return [];
1718
+            }
1719
+            $owner = $path->getOwner()->getUID();
1720
+        }
1721
+
1722
+        $providers = $this->factory->getAllProviders();
1723
+
1724
+        /** @var Node[] $nodes */
1725
+        $nodes = [];
1726
+
1727
+
1728
+        if ($currentAccess) {
1729
+            $ownerPath = $path->getPath();
1730
+            $ownerPath = explode('/', $ownerPath, 4);
1731
+            if (count($ownerPath) < 4) {
1732
+                $ownerPath = '';
1733
+            } else {
1734
+                $ownerPath = $ownerPath[3];
1735
+            }
1736
+            $al['users'][$owner] = [
1737
+                'node_id' => $path->getId(),
1738
+                'node_path' => '/' . $ownerPath,
1739
+            ];
1740
+        } else {
1741
+            $al['users'][] = $owner;
1742
+        }
1743
+
1744
+        // Collect all the shares
1745
+        while ($path->getPath() !== $userFolder->getPath()) {
1746
+            $nodes[] = $path;
1747
+            if (!$recursive) {
1748
+                break;
1749
+            }
1750
+            $path = $path->getParent();
1751
+        }
1752
+
1753
+        foreach ($providers as $provider) {
1754
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1755
+
1756
+            foreach ($tmp as $k => $v) {
1757
+                if (isset($al[$k])) {
1758
+                    if (is_array($al[$k])) {
1759
+                        if ($currentAccess) {
1760
+                            $al[$k] += $v;
1761
+                        } else {
1762
+                            $al[$k] = array_merge($al[$k], $v);
1763
+                            $al[$k] = array_unique($al[$k]);
1764
+                            $al[$k] = array_values($al[$k]);
1765
+                        }
1766
+                    } else {
1767
+                        $al[$k] = $al[$k] || $v;
1768
+                    }
1769
+                } else {
1770
+                    $al[$k] = $v;
1771
+                }
1772
+            }
1773
+        }
1774
+
1775
+        return $al;
1776
+    }
1777
+
1778
+    /**
1779
+     * Create a new share
1780
+     *
1781
+     * @return IShare
1782
+     */
1783
+    public function newShare() {
1784
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1785
+    }
1786
+
1787
+    /**
1788
+     * Is the share API enabled
1789
+     *
1790
+     * @return bool
1791
+     */
1792
+    public function shareApiEnabled() {
1793
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1794
+    }
1795
+
1796
+    /**
1797
+     * Is public link sharing enabled
1798
+     *
1799
+     * @return bool
1800
+     */
1801
+    public function shareApiAllowLinks() {
1802
+        if ($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1803
+            return false;
1804
+        }
1805
+
1806
+        $user = $this->userSession->getUser();
1807
+        if ($user) {
1808
+            $excludedGroups = json_decode($this->config->getAppValue('core', 'shareapi_allow_links_exclude_groups', '[]'));
1809
+            if ($excludedGroups) {
1810
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1811
+                return !(bool)array_intersect($excludedGroups, $userGroups);
1812
+            }
1813
+        }
1814
+
1815
+        return true;
1816
+    }
1817
+
1818
+    /**
1819
+     * Is password on public link requires
1820
+     *
1821
+     * @param bool Check group membership exclusion
1822
+     * @return bool
1823
+     */
1824
+    public function shareApiLinkEnforcePassword(bool $checkGroupMembership = true) {
1825
+        $excludedGroups = $this->config->getAppValue('core', 'shareapi_enforce_links_password_excluded_groups', '');
1826
+        if ($excludedGroups !== '' && $checkGroupMembership) {
1827
+            $excludedGroups = json_decode($excludedGroups);
1828
+            $user = $this->userSession->getUser();
1829
+            if ($user) {
1830
+                $userGroups = $this->groupManager->getUserGroupIds($user);
1831
+                if ((bool)array_intersect($excludedGroups, $userGroups)) {
1832
+                    return false;
1833
+                }
1834
+            }
1835
+        }
1836
+        return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1837
+    }
1838
+
1839
+    /**
1840
+     * Is default link expire date enabled
1841
+     *
1842
+     * @return bool
1843
+     */
1844
+    public function shareApiLinkDefaultExpireDate() {
1845
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1846
+    }
1847
+
1848
+    /**
1849
+     * Is default link expire date enforced
1850
+     *`
1851
+     *
1852
+     * @return bool
1853
+     */
1854
+    public function shareApiLinkDefaultExpireDateEnforced() {
1855
+        return $this->shareApiLinkDefaultExpireDate() &&
1856
+            $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1857
+    }
1858
+
1859
+
1860
+    /**
1861
+     * Number of default link expire days
1862
+     *
1863
+     * @return int
1864
+     */
1865
+    public function shareApiLinkDefaultExpireDays() {
1866
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1867
+    }
1868
+
1869
+    /**
1870
+     * Is default internal expire date enabled
1871
+     *
1872
+     * @return bool
1873
+     */
1874
+    public function shareApiInternalDefaultExpireDate(): bool {
1875
+        return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes';
1876
+    }
1877
+
1878
+    /**
1879
+     * Is default remote expire date enabled
1880
+     *
1881
+     * @return bool
1882
+     */
1883
+    public function shareApiRemoteDefaultExpireDate(): bool {
1884
+        return $this->config->getAppValue('core', 'shareapi_default_remote_expire_date', 'no') === 'yes';
1885
+    }
1886
+
1887
+    /**
1888
+     * Is default expire date enforced
1889
+     *
1890
+     * @return bool
1891
+     */
1892
+    public function shareApiInternalDefaultExpireDateEnforced(): bool {
1893
+        return $this->shareApiInternalDefaultExpireDate() &&
1894
+            $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
1895
+    }
1896
+
1897
+    /**
1898
+     * Is default expire date enforced for remote shares
1899
+     *
1900
+     * @return bool
1901
+     */
1902
+    public function shareApiRemoteDefaultExpireDateEnforced(): bool {
1903
+        return $this->shareApiRemoteDefaultExpireDate() &&
1904
+            $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
1905
+    }
1906
+
1907
+    /**
1908
+     * Number of default expire days
1909
+     *
1910
+     * @return int
1911
+     */
1912
+    public function shareApiInternalDefaultExpireDays(): int {
1913
+        return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7');
1914
+    }
1915
+
1916
+    /**
1917
+     * Number of default expire days for remote shares
1918
+     *
1919
+     * @return int
1920
+     */
1921
+    public function shareApiRemoteDefaultExpireDays(): int {
1922
+        return (int)$this->config->getAppValue('core', 'shareapi_remote_expire_after_n_days', '7');
1923
+    }
1924
+
1925
+    /**
1926
+     * Allow public upload on link shares
1927
+     *
1928
+     * @return bool
1929
+     */
1930
+    public function shareApiLinkAllowPublicUpload() {
1931
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1932
+    }
1933
+
1934
+    /**
1935
+     * check if user can only share with group members
1936
+     *
1937
+     * @return bool
1938
+     */
1939
+    public function shareWithGroupMembersOnly() {
1940
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1941
+    }
1942
+
1943
+    /**
1944
+     * Check if users can share with groups
1945
+     *
1946
+     * @return bool
1947
+     */
1948
+    public function allowGroupSharing() {
1949
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1950
+    }
1951
+
1952
+    public function allowEnumeration(): bool {
1953
+        return $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
1954
+    }
1955
+
1956
+    public function limitEnumerationToGroups(): bool {
1957
+        return $this->allowEnumeration() &&
1958
+            $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
1959
+    }
1960
+
1961
+    public function limitEnumerationToPhone(): bool {
1962
+        return $this->allowEnumeration() &&
1963
+            $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
1964
+    }
1965
+
1966
+    public function allowEnumerationFullMatch(): bool {
1967
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
1968
+    }
1969
+
1970
+    public function matchEmail(): bool {
1971
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
1972
+    }
1973
+
1974
+    public function ignoreSecondDisplayName(): bool {
1975
+        return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
1976
+    }
1977
+
1978
+    public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
1979
+        if ($this->allowEnumerationFullMatch()) {
1980
+            return true;
1981
+        }
1982
+
1983
+        if (!$this->allowEnumeration()) {
1984
+            return false;
1985
+        }
1986
+
1987
+        if (!$this->limitEnumerationToPhone() && !$this->limitEnumerationToGroups()) {
1988
+            // Enumeration is enabled and not restricted: OK
1989
+            return true;
1990
+        }
1991
+
1992
+        if (!$currentUser instanceof IUser) {
1993
+            // Enumeration restrictions require an account
1994
+            return false;
1995
+        }
1996
+
1997
+        // Enumeration is limited to phone match
1998
+        if ($this->limitEnumerationToPhone() && $this->knownUserService->isKnownToUser($currentUser->getUID(), $targetUser->getUID())) {
1999
+            return true;
2000
+        }
2001
+
2002
+        // Enumeration is limited to groups
2003
+        if ($this->limitEnumerationToGroups()) {
2004
+            $currentUserGroupIds = $this->groupManager->getUserGroupIds($currentUser);
2005
+            $targetUserGroupIds = $this->groupManager->getUserGroupIds($targetUser);
2006
+            if (!empty(array_intersect($currentUserGroupIds, $targetUserGroupIds))) {
2007
+                return true;
2008
+            }
2009
+        }
2010
+
2011
+        return false;
2012
+    }
2013
+
2014
+    /**
2015
+     * Copied from \OC_Util::isSharingDisabledForUser
2016
+     *
2017
+     * TODO: Deprecate function from OC_Util
2018
+     *
2019
+     * @param string $userId
2020
+     * @return bool
2021
+     */
2022
+    public function sharingDisabledForUser($userId) {
2023
+        if ($userId === null) {
2024
+            return false;
2025
+        }
2026
+
2027
+        if (isset($this->sharingDisabledForUsersCache[$userId])) {
2028
+            return $this->sharingDisabledForUsersCache[$userId];
2029
+        }
2030
+
2031
+        if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
2032
+            $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
2033
+            $excludedGroups = json_decode($groupsList);
2034
+            if (is_null($excludedGroups)) {
2035
+                $excludedGroups = explode(',', $groupsList);
2036
+                $newValue = json_encode($excludedGroups);
2037
+                $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
2038
+            }
2039
+            $user = $this->userManager->get($userId);
2040
+            $usersGroups = $this->groupManager->getUserGroupIds($user);
2041
+            if (!empty($usersGroups)) {
2042
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
2043
+                // if the user is only in groups which are disabled for sharing then
2044
+                // sharing is also disabled for the user
2045
+                if (empty($remainingGroups)) {
2046
+                    $this->sharingDisabledForUsersCache[$userId] = true;
2047
+                    return true;
2048
+                }
2049
+            }
2050
+        }
2051
+
2052
+        $this->sharingDisabledForUsersCache[$userId] = false;
2053
+        return false;
2054
+    }
2055
+
2056
+    /**
2057
+     * @inheritdoc
2058
+     */
2059
+    public function outgoingServer2ServerSharesAllowed() {
2060
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
2061
+    }
2062
+
2063
+    /**
2064
+     * @inheritdoc
2065
+     */
2066
+    public function outgoingServer2ServerGroupSharesAllowed() {
2067
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no') === 'yes';
2068
+    }
2069
+
2070
+    /**
2071
+     * @inheritdoc
2072
+     */
2073
+    public function shareProviderExists($shareType) {
2074
+        try {
2075
+            $this->factory->getProviderForType($shareType);
2076
+        } catch (ProviderException $e) {
2077
+            return false;
2078
+        }
2079
+
2080
+        return true;
2081
+    }
2082
+
2083
+    public function registerShareProvider(string $shareProviderClass): void {
2084
+        $this->factory->registerProvider($shareProviderClass);
2085
+    }
2086
+
2087
+    public function getAllShares(): iterable {
2088
+        $providers = $this->factory->getAllProviders();
2089
+
2090
+        foreach ($providers as $provider) {
2091
+            yield from $provider->getAllShares();
2092
+        }
2093
+    }
2094 2094
 }
Please login to merge, or discard this patch.