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