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