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