Completed
Push — master ( 49c622...3ff3c3 )
by Joas
17:38
created
lib/private/Share/Share.php 1 patch
Indentation   +2827 added lines, -2827 removed lines patch added patch discarded remove patch
@@ -60,2864 +60,2864 @@
 block discarded – undo
60 60
  */
61 61
 class Share extends Constants {
62 62
 
63
-	/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
64
-	 * Construct permissions for share() and setPermissions with Or (|) e.g.
65
-	 * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
66
-	 *
67
-	 * Check if permission is granted with And (&) e.g. Check if delete is
68
-	 * granted: if ($permissions & PERMISSION_DELETE)
69
-	 *
70
-	 * Remove permissions with And (&) and Not (~) e.g. Remove the update
71
-	 * permission: $permissions &= ~PERMISSION_UPDATE
72
-	 *
73
-	 * Apps are required to handle permissions on their own, this class only
74
-	 * stores and manages the permissions of shares
75
-	 * @see lib/public/constants.php
76
-	 */
77
-
78
-	/**
79
-	 * Register a sharing backend class that implements OCP\Share_Backend for an item type
80
-	 * @param string $itemType Item type
81
-	 * @param string $class Backend class
82
-	 * @param string $collectionOf (optional) Depends on item type
83
-	 * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
84
-	 * @return boolean true if backend is registered or false if error
85
-	 */
86
-	public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
87
-		if (self::isEnabled()) {
88
-			if (!isset(self::$backendTypes[$itemType])) {
89
-				self::$backendTypes[$itemType] = array(
90
-					'class' => $class,
91
-					'collectionOf' => $collectionOf,
92
-					'supportedFileExtensions' => $supportedFileExtensions
93
-				);
94
-				if(count(self::$backendTypes) === 1) {
95
-					Util::addScript('core', 'merged-share-backend');
96
-					\OC_Util::addStyle('core', 'share');
97
-				}
98
-				return true;
99
-			}
100
-			\OCP\Util::writeLog('OCP\Share',
101
-				'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
102
-				.' is already registered for '.$itemType,
103
-				\OCP\Util::WARN);
104
-		}
105
-		return false;
106
-	}
107
-
108
-	/**
109
-	 * Check if the Share API is enabled
110
-	 * @return boolean true if enabled or false
111
-	 *
112
-	 * The Share API is enabled by default if not configured
113
-	 */
114
-	public static function isEnabled() {
115
-		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
116
-			return true;
117
-		}
118
-		return false;
119
-	}
120
-
121
-	/**
122
-	 * Find which users can access a shared item
123
-	 * @param string $path to the file
124
-	 * @param string $ownerUser owner of the file
125
-	 * @param IUserManager $userManager
126
-	 * @param ILogger $logger
127
-	 * @param boolean $includeOwner include owner to the list of users with access to the file
128
-	 * @param boolean $returnUserPaths Return an array with the user => path map
129
-	 * @param boolean $recursive take all parent folders into account (default true)
130
-	 * @return array
131
-	 * @note $path needs to be relative to user data dir, e.g. 'file.txt'
132
-	 *       not '/admin/data/file.txt'
133
-	 * @throws \OC\User\NoUserException
134
-	 */
135
-	public static function getUsersSharingFile($path,
136
-											   $ownerUser,
137
-											   IUserManager $userManager,
138
-											   ILogger $logger,
139
-											   $includeOwner = false,
140
-											   $returnUserPaths = false,
141
-											   $recursive = true) {
142
-		$userObject = $userManager->get($ownerUser);
143
-
144
-		if (is_null($userObject)) {
145
-			$logger->error(
146
-				sprintf(
147
-					'Backends provided no user object for %s',
148
-					$ownerUser
149
-				),
150
-				[
151
-					'app' => 'files',
152
-				]
153
-			);
154
-			throw new \OC\User\NoUserException('Backends provided no user object');
155
-		}
156
-
157
-		$ownerUser = $userObject->getUID();
158
-
159
-		Filesystem::initMountPoints($ownerUser);
160
-		$shares = $sharePaths = $fileTargets = array();
161
-		$publicShare = false;
162
-		$remoteShare = false;
163
-		$source = -1;
164
-		$cache = $mountPath = false;
165
-
166
-		$view = new \OC\Files\View('/' . $ownerUser . '/files');
167
-		$meta = $view->getFileInfo($path);
168
-		if ($meta) {
169
-			$path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
170
-		} else {
171
-			// if the file doesn't exists yet we start with the parent folder
172
-			$meta = $view->getFileInfo(dirname($path));
173
-		}
174
-
175
-		if($meta !== false) {
176
-			$source = $meta['fileid'];
177
-			$cache = new \OC\Files\Cache\Cache($meta['storage']);
178
-
179
-			$mountPath = $meta->getMountPoint()->getMountPoint();
180
-			if ($mountPath !== false) {
181
-				$mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
182
-			}
183
-		}
184
-
185
-		$paths = [];
186
-		while ($source !== -1) {
187
-			// Fetch all shares with another user
188
-			if (!$returnUserPaths) {
189
-				$query = \OC_DB::prepare(
190
-					'SELECT `share_with`, `file_source`, `file_target`
63
+    /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
64
+     * Construct permissions for share() and setPermissions with Or (|) e.g.
65
+     * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
66
+     *
67
+     * Check if permission is granted with And (&) e.g. Check if delete is
68
+     * granted: if ($permissions & PERMISSION_DELETE)
69
+     *
70
+     * Remove permissions with And (&) and Not (~) e.g. Remove the update
71
+     * permission: $permissions &= ~PERMISSION_UPDATE
72
+     *
73
+     * Apps are required to handle permissions on their own, this class only
74
+     * stores and manages the permissions of shares
75
+     * @see lib/public/constants.php
76
+     */
77
+
78
+    /**
79
+     * Register a sharing backend class that implements OCP\Share_Backend for an item type
80
+     * @param string $itemType Item type
81
+     * @param string $class Backend class
82
+     * @param string $collectionOf (optional) Depends on item type
83
+     * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
84
+     * @return boolean true if backend is registered or false if error
85
+     */
86
+    public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
87
+        if (self::isEnabled()) {
88
+            if (!isset(self::$backendTypes[$itemType])) {
89
+                self::$backendTypes[$itemType] = array(
90
+                    'class' => $class,
91
+                    'collectionOf' => $collectionOf,
92
+                    'supportedFileExtensions' => $supportedFileExtensions
93
+                );
94
+                if(count(self::$backendTypes) === 1) {
95
+                    Util::addScript('core', 'merged-share-backend');
96
+                    \OC_Util::addStyle('core', 'share');
97
+                }
98
+                return true;
99
+            }
100
+            \OCP\Util::writeLog('OCP\Share',
101
+                'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
102
+                .' is already registered for '.$itemType,
103
+                \OCP\Util::WARN);
104
+        }
105
+        return false;
106
+    }
107
+
108
+    /**
109
+     * Check if the Share API is enabled
110
+     * @return boolean true if enabled or false
111
+     *
112
+     * The Share API is enabled by default if not configured
113
+     */
114
+    public static function isEnabled() {
115
+        if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
116
+            return true;
117
+        }
118
+        return false;
119
+    }
120
+
121
+    /**
122
+     * Find which users can access a shared item
123
+     * @param string $path to the file
124
+     * @param string $ownerUser owner of the file
125
+     * @param IUserManager $userManager
126
+     * @param ILogger $logger
127
+     * @param boolean $includeOwner include owner to the list of users with access to the file
128
+     * @param boolean $returnUserPaths Return an array with the user => path map
129
+     * @param boolean $recursive take all parent folders into account (default true)
130
+     * @return array
131
+     * @note $path needs to be relative to user data dir, e.g. 'file.txt'
132
+     *       not '/admin/data/file.txt'
133
+     * @throws \OC\User\NoUserException
134
+     */
135
+    public static function getUsersSharingFile($path,
136
+                                                $ownerUser,
137
+                                                IUserManager $userManager,
138
+                                                ILogger $logger,
139
+                                                $includeOwner = false,
140
+                                                $returnUserPaths = false,
141
+                                                $recursive = true) {
142
+        $userObject = $userManager->get($ownerUser);
143
+
144
+        if (is_null($userObject)) {
145
+            $logger->error(
146
+                sprintf(
147
+                    'Backends provided no user object for %s',
148
+                    $ownerUser
149
+                ),
150
+                [
151
+                    'app' => 'files',
152
+                ]
153
+            );
154
+            throw new \OC\User\NoUserException('Backends provided no user object');
155
+        }
156
+
157
+        $ownerUser = $userObject->getUID();
158
+
159
+        Filesystem::initMountPoints($ownerUser);
160
+        $shares = $sharePaths = $fileTargets = array();
161
+        $publicShare = false;
162
+        $remoteShare = false;
163
+        $source = -1;
164
+        $cache = $mountPath = false;
165
+
166
+        $view = new \OC\Files\View('/' . $ownerUser . '/files');
167
+        $meta = $view->getFileInfo($path);
168
+        if ($meta) {
169
+            $path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files'));
170
+        } else {
171
+            // if the file doesn't exists yet we start with the parent folder
172
+            $meta = $view->getFileInfo(dirname($path));
173
+        }
174
+
175
+        if($meta !== false) {
176
+            $source = $meta['fileid'];
177
+            $cache = new \OC\Files\Cache\Cache($meta['storage']);
178
+
179
+            $mountPath = $meta->getMountPoint()->getMountPoint();
180
+            if ($mountPath !== false) {
181
+                $mountPath = substr($mountPath, strlen('/' . $ownerUser . '/files'));
182
+            }
183
+        }
184
+
185
+        $paths = [];
186
+        while ($source !== -1) {
187
+            // Fetch all shares with another user
188
+            if (!$returnUserPaths) {
189
+                $query = \OC_DB::prepare(
190
+                    'SELECT `share_with`, `file_source`, `file_target`
191 191
 					FROM
192 192
 					`*PREFIX*share`
193 193
 					WHERE
194 194
 					`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
195
-				);
196
-				$result = $query->execute(array($source, self::SHARE_TYPE_USER));
197
-			} else {
198
-				$query = \OC_DB::prepare(
199
-					'SELECT `share_with`, `file_source`, `file_target`
195
+                );
196
+                $result = $query->execute(array($source, self::SHARE_TYPE_USER));
197
+            } else {
198
+                $query = \OC_DB::prepare(
199
+                    'SELECT `share_with`, `file_source`, `file_target`
200 200
 				FROM
201 201
 				`*PREFIX*share`
202 202
 				WHERE
203 203
 				`item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')'
204
-				);
205
-				$result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
206
-			}
207
-
208
-			if (\OCP\DB::isError($result)) {
209
-				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
210
-			} else {
211
-				while ($row = $result->fetchRow()) {
212
-					$shares[] = $row['share_with'];
213
-					if ($returnUserPaths) {
214
-						$fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
215
-					}
216
-				}
217
-			}
218
-
219
-			// We also need to take group shares into account
220
-			$query = \OC_DB::prepare(
221
-				'SELECT `share_with`, `file_source`, `file_target`
204
+                );
205
+                $result = $query->execute(array($source, self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
206
+            }
207
+
208
+            if (\OCP\DB::isError($result)) {
209
+                \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
210
+            } else {
211
+                while ($row = $result->fetchRow()) {
212
+                    $shares[] = $row['share_with'];
213
+                    if ($returnUserPaths) {
214
+                        $fileTargets[(int) $row['file_source']][$row['share_with']] = $row;
215
+                    }
216
+                }
217
+            }
218
+
219
+            // We also need to take group shares into account
220
+            $query = \OC_DB::prepare(
221
+                'SELECT `share_with`, `file_source`, `file_target`
222 222
 				FROM
223 223
 				`*PREFIX*share`
224 224
 				WHERE
225 225
 				`item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')'
226
-			);
227
-
228
-			$result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
229
-
230
-			if (\OCP\DB::isError($result)) {
231
-				\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
232
-			} else {
233
-				$groupManager = \OC::$server->getGroupManager();
234
-				while ($row = $result->fetchRow()) {
235
-
236
-					$usersInGroup = [];
237
-					$group = $groupManager->get($row['share_with']);
238
-					if ($group) {
239
-						$users = $group->searchUsers('', -1, 0);
240
-						$userIds = array();
241
-						foreach ($users as $user) {
242
-							$userIds[] = $user->getUID();
243
-						}
244
-						$usersInGroup = $userIds;
245
-					}
246
-					$shares = array_merge($shares, $usersInGroup);
247
-					if ($returnUserPaths) {
248
-						foreach ($usersInGroup as $user) {
249
-							if (!isset($fileTargets[(int) $row['file_source']][$user])) {
250
-								// When the user already has an entry for this file source
251
-								// the file is either shared directly with him as well, or
252
-								// he has an exception entry (because of naming conflict).
253
-								$fileTargets[(int) $row['file_source']][$user] = $row;
254
-							}
255
-						}
256
-					}
257
-				}
258
-			}
259
-
260
-			//check for public link shares
261
-			if (!$publicShare) {
262
-				$query = \OC_DB::prepare('
226
+            );
227
+
228
+            $result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
229
+
230
+            if (\OCP\DB::isError($result)) {
231
+                \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
232
+            } else {
233
+                $groupManager = \OC::$server->getGroupManager();
234
+                while ($row = $result->fetchRow()) {
235
+
236
+                    $usersInGroup = [];
237
+                    $group = $groupManager->get($row['share_with']);
238
+                    if ($group) {
239
+                        $users = $group->searchUsers('', -1, 0);
240
+                        $userIds = array();
241
+                        foreach ($users as $user) {
242
+                            $userIds[] = $user->getUID();
243
+                        }
244
+                        $usersInGroup = $userIds;
245
+                    }
246
+                    $shares = array_merge($shares, $usersInGroup);
247
+                    if ($returnUserPaths) {
248
+                        foreach ($usersInGroup as $user) {
249
+                            if (!isset($fileTargets[(int) $row['file_source']][$user])) {
250
+                                // When the user already has an entry for this file source
251
+                                // the file is either shared directly with him as well, or
252
+                                // he has an exception entry (because of naming conflict).
253
+                                $fileTargets[(int) $row['file_source']][$user] = $row;
254
+                            }
255
+                        }
256
+                    }
257
+                }
258
+            }
259
+
260
+            //check for public link shares
261
+            if (!$publicShare) {
262
+                $query = \OC_DB::prepare('
263 263
 					SELECT `share_with`
264 264
 					FROM `*PREFIX*share`
265 265
 					WHERE `item_source` = ? AND `share_type` IN (?, ?) AND `item_type` IN (\'file\', \'folder\')', 1
266
-				);
267
-
268
-				$result = $query->execute(array($source, self::SHARE_TYPE_LINK, self::SHARE_TYPE_EMAIL));
269
-
270
-				if (\OCP\DB::isError($result)) {
271
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
272
-				} else {
273
-					if ($result->fetchRow()) {
274
-						$publicShare = true;
275
-					}
276
-				}
277
-			}
278
-
279
-			//check for remote share
280
-			if (!$remoteShare) {
281
-				$query = \OC_DB::prepare('
266
+                );
267
+
268
+                $result = $query->execute(array($source, self::SHARE_TYPE_LINK, self::SHARE_TYPE_EMAIL));
269
+
270
+                if (\OCP\DB::isError($result)) {
271
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
272
+                } else {
273
+                    if ($result->fetchRow()) {
274
+                        $publicShare = true;
275
+                    }
276
+                }
277
+            }
278
+
279
+            //check for remote share
280
+            if (!$remoteShare) {
281
+                $query = \OC_DB::prepare('
282 282
 					SELECT `share_with`
283 283
 					FROM `*PREFIX*share`
284 284
 					WHERE `item_source` = ? AND `share_type` = ? AND `item_type` IN (\'file\', \'folder\')', 1
285
-				);
286
-
287
-				$result = $query->execute(array($source, self::SHARE_TYPE_REMOTE));
288
-
289
-				if (\OCP\DB::isError($result)) {
290
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
291
-				} else {
292
-					if ($result->fetchRow()) {
293
-						$remoteShare = true;
294
-					}
295
-				}
296
-			}
297
-
298
-			// let's get the parent for the next round
299
-			$meta = $cache->get((int)$source);
300
-			if ($recursive === true && $meta !== false) {
301
-				$paths[$source] = $meta['path'];
302
-				$source = (int)$meta['parent'];
303
-			} else {
304
-				$source = -1;
305
-			}
306
-		}
307
-
308
-		// Include owner in list of users, if requested
309
-		if ($includeOwner) {
310
-			$shares[] = $ownerUser;
311
-		}
312
-
313
-		if ($returnUserPaths) {
314
-			$fileTargetIDs = array_keys($fileTargets);
315
-			$fileTargetIDs = array_unique($fileTargetIDs);
316
-
317
-			if (!empty($fileTargetIDs)) {
318
-				$query = \OC_DB::prepare(
319
-					'SELECT `fileid`, `path`
285
+                );
286
+
287
+                $result = $query->execute(array($source, self::SHARE_TYPE_REMOTE));
288
+
289
+                if (\OCP\DB::isError($result)) {
290
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
291
+                } else {
292
+                    if ($result->fetchRow()) {
293
+                        $remoteShare = true;
294
+                    }
295
+                }
296
+            }
297
+
298
+            // let's get the parent for the next round
299
+            $meta = $cache->get((int)$source);
300
+            if ($recursive === true && $meta !== false) {
301
+                $paths[$source] = $meta['path'];
302
+                $source = (int)$meta['parent'];
303
+            } else {
304
+                $source = -1;
305
+            }
306
+        }
307
+
308
+        // Include owner in list of users, if requested
309
+        if ($includeOwner) {
310
+            $shares[] = $ownerUser;
311
+        }
312
+
313
+        if ($returnUserPaths) {
314
+            $fileTargetIDs = array_keys($fileTargets);
315
+            $fileTargetIDs = array_unique($fileTargetIDs);
316
+
317
+            if (!empty($fileTargetIDs)) {
318
+                $query = \OC_DB::prepare(
319
+                    'SELECT `fileid`, `path`
320 320
 					FROM `*PREFIX*filecache`
321 321
 					WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')'
322
-				);
323
-				$result = $query->execute();
324
-
325
-				if (\OCP\DB::isError($result)) {
326
-					\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
327
-				} else {
328
-					while ($row = $result->fetchRow()) {
329
-						foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
330
-							if ($mountPath !== false) {
331
-								$sharedPath = $shareData['file_target'];
332
-								$sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']]));
333
-								$sharePaths[$uid] = $sharedPath;
334
-							} else {
335
-								$sharedPath = $shareData['file_target'];
336
-								$sharedPath .= substr($path, strlen($row['path']) -5);
337
-								$sharePaths[$uid] = $sharedPath;
338
-							}
339
-						}
340
-					}
341
-				}
342
-			}
343
-
344
-			if ($includeOwner) {
345
-				$sharePaths[$ownerUser] = $path;
346
-			} else {
347
-				unset($sharePaths[$ownerUser]);
348
-			}
349
-
350
-			return $sharePaths;
351
-		}
352
-
353
-		return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare);
354
-	}
355
-
356
-	/**
357
-	 * Get the items of item type shared with the current user
358
-	 * @param string $itemType
359
-	 * @param int $format (optional) Format type must be defined by the backend
360
-	 * @param mixed $parameters (optional)
361
-	 * @param int $limit Number of items to return (optional) Returns all by default
362
-	 * @param boolean $includeCollections (optional)
363
-	 * @return mixed Return depends on format
364
-	 */
365
-	public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
366
-											  $parameters = null, $limit = -1, $includeCollections = false) {
367
-		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
368
-			$parameters, $limit, $includeCollections);
369
-	}
370
-
371
-	/**
372
-	 * Get the items of item type shared with a user
373
-	 * @param string $itemType
374
-	 * @param string $user id for which user we want the shares
375
-	 * @param int $format (optional) Format type must be defined by the backend
376
-	 * @param mixed $parameters (optional)
377
-	 * @param int $limit Number of items to return (optional) Returns all by default
378
-	 * @param boolean $includeCollections (optional)
379
-	 * @return mixed Return depends on format
380
-	 */
381
-	public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
382
-												  $parameters = null, $limit = -1, $includeCollections = false) {
383
-		return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
384
-			$parameters, $limit, $includeCollections);
385
-	}
386
-
387
-	/**
388
-	 * Get the item of item type shared with the current user
389
-	 * @param string $itemType
390
-	 * @param string $itemTarget
391
-	 * @param int $format (optional) Format type must be defined by the backend
392
-	 * @param mixed $parameters (optional)
393
-	 * @param boolean $includeCollections (optional)
394
-	 * @return mixed Return depends on format
395
-	 */
396
-	public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
397
-											 $parameters = null, $includeCollections = false) {
398
-		return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
399
-			$parameters, 1, $includeCollections);
400
-	}
401
-
402
-	/**
403
-	 * Get the item of item type shared with a given user by source
404
-	 * @param string $itemType
405
-	 * @param string $itemSource
406
-	 * @param string $user User to whom the item was shared
407
-	 * @param string $owner Owner of the share
408
-	 * @param int $shareType only look for a specific share type
409
-	 * @return array Return list of items with file_target, permissions and expiration
410
-	 */
411
-	public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
412
-		$shares = array();
413
-		$fileDependent = false;
414
-
415
-		$where = 'WHERE';
416
-		$fileDependentWhere = '';
417
-		if ($itemType === 'file' || $itemType === 'folder') {
418
-			$fileDependent = true;
419
-			$column = 'file_source';
420
-			$fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
421
-			$fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
422
-		} else {
423
-			$column = 'item_source';
424
-		}
425
-
426
-		$select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
427
-
428
-		$where .= ' `' . $column . '` = ? AND `item_type` = ? ';
429
-		$arguments = array($itemSource, $itemType);
430
-		// for link shares $user === null
431
-		if ($user !== null) {
432
-			$where .= ' AND `share_with` = ? ';
433
-			$arguments[] = $user;
434
-		}
435
-
436
-		if ($shareType !== null) {
437
-			$where .= ' AND `share_type` = ? ';
438
-			$arguments[] = $shareType;
439
-		}
440
-
441
-		if ($owner !== null) {
442
-			$where .= ' AND `uid_owner` = ? ';
443
-			$arguments[] = $owner;
444
-		}
445
-
446
-		$query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
447
-
448
-		$result = \OC_DB::executeAudited($query, $arguments);
449
-
450
-		while ($row = $result->fetchRow()) {
451
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
452
-				continue;
453
-			}
454
-			if ($fileDependent && (int)$row['file_parent'] === -1) {
455
-				// if it is a mount point we need to get the path from the mount manager
456
-				$mountManager = \OC\Files\Filesystem::getMountManager();
457
-				$mountPoint = $mountManager->findByStorageId($row['storage_id']);
458
-				if (!empty($mountPoint)) {
459
-					$path = $mountPoint[0]->getMountPoint();
460
-					$path = trim($path, '/');
461
-					$path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
462
-					$row['path'] = $path;
463
-				} else {
464
-					\OC::$server->getLogger()->warning(
465
-						'Could not resolve mount point for ' . $row['storage_id'],
466
-						['app' => 'OCP\Share']
467
-					);
468
-				}
469
-			}
470
-			$shares[] = $row;
471
-		}
472
-
473
-		//if didn't found a result than let's look for a group share.
474
-		if(empty($shares) && $user !== null) {
475
-			$userObject = \OC::$server->getUserManager()->get($user);
476
-			$groups = [];
477
-			if ($userObject) {
478
-				$groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
479
-			}
480
-
481
-			if (!empty($groups)) {
482
-				$where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
483
-				$arguments = array($itemSource, $itemType, $groups);
484
-				$types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
485
-
486
-				if ($owner !== null) {
487
-					$where .= ' AND `uid_owner` = ?';
488
-					$arguments[] = $owner;
489
-					$types[] = null;
490
-				}
491
-
492
-				// TODO: inject connection, hopefully one day in the future when this
493
-				// class isn't static anymore...
494
-				$conn = \OC::$server->getDatabaseConnection();
495
-				$result = $conn->executeQuery(
496
-					'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
497
-					$arguments,
498
-					$types
499
-				);
500
-
501
-				while ($row = $result->fetch()) {
502
-					$shares[] = $row;
503
-				}
504
-			}
505
-		}
506
-
507
-		return $shares;
508
-
509
-	}
510
-
511
-	/**
512
-	 * Get the item of item type shared with the current user by source
513
-	 * @param string $itemType
514
-	 * @param string $itemSource
515
-	 * @param int $format (optional) Format type must be defined by the backend
516
-	 * @param mixed $parameters
517
-	 * @param boolean $includeCollections
518
-	 * @param string $shareWith (optional) define against which user should be checked, default: current user
519
-	 * @return array
520
-	 */
521
-	public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
522
-													 $parameters = null, $includeCollections = false, $shareWith = null) {
523
-		$shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
524
-		return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
525
-			$parameters, 1, $includeCollections, true);
526
-	}
527
-
528
-	/**
529
-	 * Get the item of item type shared by a link
530
-	 * @param string $itemType
531
-	 * @param string $itemSource
532
-	 * @param string $uidOwner Owner of link
533
-	 * @return array
534
-	 */
535
-	public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
536
-		return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
537
-			null, 1);
538
-	}
539
-
540
-	/**
541
-	 * Based on the given token the share information will be returned - password protected shares will be verified
542
-	 * @param string $token
543
-	 * @param bool $checkPasswordProtection
544
-	 * @return array|boolean false will be returned in case the token is unknown or unauthorized
545
-	 */
546
-	public static function getShareByToken($token, $checkPasswordProtection = true) {
547
-		$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
548
-		$result = $query->execute(array($token));
549
-		if ($result === false) {
550
-			\OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
551
-		}
552
-		$row = $result->fetchRow();
553
-		if ($row === false) {
554
-			return false;
555
-		}
556
-		if (is_array($row) and self::expireItem($row)) {
557
-			return false;
558
-		}
559
-
560
-		// password protected shares need to be authenticated
561
-		if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
562
-			return false;
563
-		}
564
-
565
-		return $row;
566
-	}
567
-
568
-	/**
569
-	 * resolves reshares down to the last real share
570
-	 * @param array $linkItem
571
-	 * @return array file owner
572
-	 */
573
-	public static function resolveReShare($linkItem)
574
-	{
575
-		if (isset($linkItem['parent'])) {
576
-			$parent = $linkItem['parent'];
577
-			while (isset($parent)) {
578
-				$query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
579
-				$item = $query->execute(array($parent))->fetchRow();
580
-				if (isset($item['parent'])) {
581
-					$parent = $item['parent'];
582
-				} else {
583
-					return $item;
584
-				}
585
-			}
586
-		}
587
-		return $linkItem;
588
-	}
589
-
590
-
591
-	/**
592
-	 * Get the shared items of item type owned by the current user
593
-	 * @param string $itemType
594
-	 * @param int $format (optional) Format type must be defined by the backend
595
-	 * @param mixed $parameters
596
-	 * @param int $limit Number of items to return (optional) Returns all by default
597
-	 * @param boolean $includeCollections
598
-	 * @return mixed Return depends on format
599
-	 */
600
-	public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
601
-										  $limit = -1, $includeCollections = false) {
602
-		return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
603
-			$parameters, $limit, $includeCollections);
604
-	}
605
-
606
-	/**
607
-	 * Get the shared item of item type owned by the current user
608
-	 * @param string $itemType
609
-	 * @param string $itemSource
610
-	 * @param int $format (optional) Format type must be defined by the backend
611
-	 * @param mixed $parameters
612
-	 * @param boolean $includeCollections
613
-	 * @return mixed Return depends on format
614
-	 */
615
-	public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
616
-										 $parameters = null, $includeCollections = false) {
617
-		return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
618
-			$parameters, -1, $includeCollections);
619
-	}
620
-
621
-	/**
622
-	 * Get all users an item is shared with
623
-	 * @param string $itemType
624
-	 * @param string $itemSource
625
-	 * @param string $uidOwner
626
-	 * @param boolean $includeCollections
627
-	 * @param boolean $checkExpireDate
628
-	 * @return array Return array of users
629
-	 */
630
-	public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
631
-
632
-		$users = array();
633
-		$items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
634
-		if ($items) {
635
-			foreach ($items as $item) {
636
-				if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
637
-					$users[] = $item['share_with'];
638
-				} else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
639
-
640
-					$group = \OC::$server->getGroupManager()->get($item['share_with']);
641
-					$userIds = [];
642
-					if ($group) {
643
-						$users = $group->searchUsers('', -1, 0);
644
-						foreach ($users as $user) {
645
-							$userIds[] = $user->getUID();
646
-						}
647
-						return $userIds;
648
-					}
649
-
650
-					$users = array_merge($users, $userIds);
651
-				}
652
-			}
653
-		}
654
-		return $users;
655
-	}
656
-
657
-	/**
658
-	 * Share an item with a user, group, or via private link
659
-	 * @param string $itemType
660
-	 * @param string $itemSource
661
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
662
-	 * @param string $shareWith User or group the item is being shared with
663
-	 * @param int $permissions CRUDS
664
-	 * @param string $itemSourceName
665
-	 * @param \DateTime $expirationDate
666
-	 * @param bool $passwordChanged
667
-	 * @return boolean|string Returns true on success or false on failure, Returns token on success for links
668
-	 * @throws \OC\HintException when the share type is remote and the shareWith is invalid
669
-	 * @throws \Exception
670
-	 */
671
-	public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
672
-
673
-		$backend = self::getBackend($itemType);
674
-		$l = \OC::$server->getL10N('lib');
675
-
676
-		if ($backend->isShareTypeAllowed($shareType) === false) {
677
-			$message = 'Sharing %s failed, because the backend does not allow shares from type %i';
678
-			$message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
679
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
680
-			throw new \Exception($message_t);
681
-		}
682
-
683
-		$uidOwner = \OC_User::getUser();
684
-		$shareWithinGroupOnly = self::shareWithGroupMembersOnly();
685
-
686
-		if (is_null($itemSourceName)) {
687
-			$itemSourceName = $itemSource;
688
-		}
689
-		$itemName = $itemSourceName;
690
-
691
-		// check if file can be shared
692
-		if ($itemType === 'file' or $itemType === 'folder') {
693
-			$path = \OC\Files\Filesystem::getPath($itemSource);
694
-			$itemName = $path;
695
-
696
-			// verify that the file exists before we try to share it
697
-			if (!$path) {
698
-				$message = 'Sharing %s failed, because the file does not exist';
699
-				$message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
700
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
701
-				throw new \Exception($message_t);
702
-			}
703
-			// verify that the user has share permission
704
-			if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) {
705
-				$message = 'You are not allowed to share %s';
706
-				$message_t = $l->t('You are not allowed to share %s', [$path]);
707
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
708
-				throw new \Exception($message_t);
709
-			}
710
-		}
711
-
712
-		//verify that we don't share a folder which already contains a share mount point
713
-		if ($itemType === 'folder') {
714
-			$path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
715
-			$mountManager = \OC\Files\Filesystem::getMountManager();
716
-			$mounts = $mountManager->findIn($path);
717
-			foreach ($mounts as $mount) {
718
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
719
-					$message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
720
-					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
721
-					throw new \Exception($message);
722
-				}
723
-
724
-			}
725
-		}
726
-
727
-		// single file shares should never have delete permissions
728
-		if ($itemType === 'file') {
729
-			$permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
730
-		}
731
-
732
-		//Validate expirationDate
733
-		if ($expirationDate !== null) {
734
-			try {
735
-				/*
322
+                );
323
+                $result = $query->execute();
324
+
325
+                if (\OCP\DB::isError($result)) {
326
+                    \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage(), \OCP\Util::ERROR);
327
+                } else {
328
+                    while ($row = $result->fetchRow()) {
329
+                        foreach ($fileTargets[$row['fileid']] as $uid => $shareData) {
330
+                            if ($mountPath !== false) {
331
+                                $sharedPath = $shareData['file_target'];
332
+                                $sharedPath .= substr($path, strlen($mountPath) + strlen($paths[$row['fileid']]));
333
+                                $sharePaths[$uid] = $sharedPath;
334
+                            } else {
335
+                                $sharedPath = $shareData['file_target'];
336
+                                $sharedPath .= substr($path, strlen($row['path']) -5);
337
+                                $sharePaths[$uid] = $sharedPath;
338
+                            }
339
+                        }
340
+                    }
341
+                }
342
+            }
343
+
344
+            if ($includeOwner) {
345
+                $sharePaths[$ownerUser] = $path;
346
+            } else {
347
+                unset($sharePaths[$ownerUser]);
348
+            }
349
+
350
+            return $sharePaths;
351
+        }
352
+
353
+        return array('users' => array_unique($shares), 'public' => $publicShare, 'remote' => $remoteShare);
354
+    }
355
+
356
+    /**
357
+     * Get the items of item type shared with the current user
358
+     * @param string $itemType
359
+     * @param int $format (optional) Format type must be defined by the backend
360
+     * @param mixed $parameters (optional)
361
+     * @param int $limit Number of items to return (optional) Returns all by default
362
+     * @param boolean $includeCollections (optional)
363
+     * @return mixed Return depends on format
364
+     */
365
+    public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
366
+                                                $parameters = null, $limit = -1, $includeCollections = false) {
367
+        return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
368
+            $parameters, $limit, $includeCollections);
369
+    }
370
+
371
+    /**
372
+     * Get the items of item type shared with a user
373
+     * @param string $itemType
374
+     * @param string $user id for which user we want the shares
375
+     * @param int $format (optional) Format type must be defined by the backend
376
+     * @param mixed $parameters (optional)
377
+     * @param int $limit Number of items to return (optional) Returns all by default
378
+     * @param boolean $includeCollections (optional)
379
+     * @return mixed Return depends on format
380
+     */
381
+    public static function getItemsSharedWithUser($itemType, $user, $format = self::FORMAT_NONE,
382
+                                                    $parameters = null, $limit = -1, $includeCollections = false) {
383
+        return self::getItems($itemType, null, self::$shareTypeUserAndGroups, $user, null, $format,
384
+            $parameters, $limit, $includeCollections);
385
+    }
386
+
387
+    /**
388
+     * Get the item of item type shared with the current user
389
+     * @param string $itemType
390
+     * @param string $itemTarget
391
+     * @param int $format (optional) Format type must be defined by the backend
392
+     * @param mixed $parameters (optional)
393
+     * @param boolean $includeCollections (optional)
394
+     * @return mixed Return depends on format
395
+     */
396
+    public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
397
+                                                $parameters = null, $includeCollections = false) {
398
+        return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
399
+            $parameters, 1, $includeCollections);
400
+    }
401
+
402
+    /**
403
+     * Get the item of item type shared with a given user by source
404
+     * @param string $itemType
405
+     * @param string $itemSource
406
+     * @param string $user User to whom the item was shared
407
+     * @param string $owner Owner of the share
408
+     * @param int $shareType only look for a specific share type
409
+     * @return array Return list of items with file_target, permissions and expiration
410
+     */
411
+    public static function getItemSharedWithUser($itemType, $itemSource, $user, $owner = null, $shareType = null) {
412
+        $shares = array();
413
+        $fileDependent = false;
414
+
415
+        $where = 'WHERE';
416
+        $fileDependentWhere = '';
417
+        if ($itemType === 'file' || $itemType === 'folder') {
418
+            $fileDependent = true;
419
+            $column = 'file_source';
420
+            $fileDependentWhere = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
421
+            $fileDependentWhere .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
422
+        } else {
423
+            $column = 'item_source';
424
+        }
425
+
426
+        $select = self::createSelectStatement(self::FORMAT_NONE, $fileDependent);
427
+
428
+        $where .= ' `' . $column . '` = ? AND `item_type` = ? ';
429
+        $arguments = array($itemSource, $itemType);
430
+        // for link shares $user === null
431
+        if ($user !== null) {
432
+            $where .= ' AND `share_with` = ? ';
433
+            $arguments[] = $user;
434
+        }
435
+
436
+        if ($shareType !== null) {
437
+            $where .= ' AND `share_type` = ? ';
438
+            $arguments[] = $shareType;
439
+        }
440
+
441
+        if ($owner !== null) {
442
+            $where .= ' AND `uid_owner` = ? ';
443
+            $arguments[] = $owner;
444
+        }
445
+
446
+        $query = \OC_DB::prepare('SELECT ' . $select . ' FROM `*PREFIX*share` '. $fileDependentWhere . $where);
447
+
448
+        $result = \OC_DB::executeAudited($query, $arguments);
449
+
450
+        while ($row = $result->fetchRow()) {
451
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
452
+                continue;
453
+            }
454
+            if ($fileDependent && (int)$row['file_parent'] === -1) {
455
+                // if it is a mount point we need to get the path from the mount manager
456
+                $mountManager = \OC\Files\Filesystem::getMountManager();
457
+                $mountPoint = $mountManager->findByStorageId($row['storage_id']);
458
+                if (!empty($mountPoint)) {
459
+                    $path = $mountPoint[0]->getMountPoint();
460
+                    $path = trim($path, '/');
461
+                    $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt`
462
+                    $row['path'] = $path;
463
+                } else {
464
+                    \OC::$server->getLogger()->warning(
465
+                        'Could not resolve mount point for ' . $row['storage_id'],
466
+                        ['app' => 'OCP\Share']
467
+                    );
468
+                }
469
+            }
470
+            $shares[] = $row;
471
+        }
472
+
473
+        //if didn't found a result than let's look for a group share.
474
+        if(empty($shares) && $user !== null) {
475
+            $userObject = \OC::$server->getUserManager()->get($user);
476
+            $groups = [];
477
+            if ($userObject) {
478
+                $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject);
479
+            }
480
+
481
+            if (!empty($groups)) {
482
+                $where = $fileDependentWhere . ' WHERE `' . $column . '` = ? AND `item_type` = ? AND `share_with` in (?)';
483
+                $arguments = array($itemSource, $itemType, $groups);
484
+                $types = array(null, null, IQueryBuilder::PARAM_STR_ARRAY);
485
+
486
+                if ($owner !== null) {
487
+                    $where .= ' AND `uid_owner` = ?';
488
+                    $arguments[] = $owner;
489
+                    $types[] = null;
490
+                }
491
+
492
+                // TODO: inject connection, hopefully one day in the future when this
493
+                // class isn't static anymore...
494
+                $conn = \OC::$server->getDatabaseConnection();
495
+                $result = $conn->executeQuery(
496
+                    'SELECT ' . $select . ' FROM `*PREFIX*share` ' . $where,
497
+                    $arguments,
498
+                    $types
499
+                );
500
+
501
+                while ($row = $result->fetch()) {
502
+                    $shares[] = $row;
503
+                }
504
+            }
505
+        }
506
+
507
+        return $shares;
508
+
509
+    }
510
+
511
+    /**
512
+     * Get the item of item type shared with the current user by source
513
+     * @param string $itemType
514
+     * @param string $itemSource
515
+     * @param int $format (optional) Format type must be defined by the backend
516
+     * @param mixed $parameters
517
+     * @param boolean $includeCollections
518
+     * @param string $shareWith (optional) define against which user should be checked, default: current user
519
+     * @return array
520
+     */
521
+    public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
522
+                                                        $parameters = null, $includeCollections = false, $shareWith = null) {
523
+        $shareWith = ($shareWith === null) ? \OC_User::getUser() : $shareWith;
524
+        return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, $format,
525
+            $parameters, 1, $includeCollections, true);
526
+    }
527
+
528
+    /**
529
+     * Get the item of item type shared by a link
530
+     * @param string $itemType
531
+     * @param string $itemSource
532
+     * @param string $uidOwner Owner of link
533
+     * @return array
534
+     */
535
+    public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
536
+        return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
537
+            null, 1);
538
+    }
539
+
540
+    /**
541
+     * Based on the given token the share information will be returned - password protected shares will be verified
542
+     * @param string $token
543
+     * @param bool $checkPasswordProtection
544
+     * @return array|boolean false will be returned in case the token is unknown or unauthorized
545
+     */
546
+    public static function getShareByToken($token, $checkPasswordProtection = true) {
547
+        $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
548
+        $result = $query->execute(array($token));
549
+        if ($result === false) {
550
+            \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage() . ', token=' . $token, \OCP\Util::ERROR);
551
+        }
552
+        $row = $result->fetchRow();
553
+        if ($row === false) {
554
+            return false;
555
+        }
556
+        if (is_array($row) and self::expireItem($row)) {
557
+            return false;
558
+        }
559
+
560
+        // password protected shares need to be authenticated
561
+        if ($checkPasswordProtection && !\OCP\Share::checkPasswordProtectedShare($row)) {
562
+            return false;
563
+        }
564
+
565
+        return $row;
566
+    }
567
+
568
+    /**
569
+     * resolves reshares down to the last real share
570
+     * @param array $linkItem
571
+     * @return array file owner
572
+     */
573
+    public static function resolveReShare($linkItem)
574
+    {
575
+        if (isset($linkItem['parent'])) {
576
+            $parent = $linkItem['parent'];
577
+            while (isset($parent)) {
578
+                $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
579
+                $item = $query->execute(array($parent))->fetchRow();
580
+                if (isset($item['parent'])) {
581
+                    $parent = $item['parent'];
582
+                } else {
583
+                    return $item;
584
+                }
585
+            }
586
+        }
587
+        return $linkItem;
588
+    }
589
+
590
+
591
+    /**
592
+     * Get the shared items of item type owned by the current user
593
+     * @param string $itemType
594
+     * @param int $format (optional) Format type must be defined by the backend
595
+     * @param mixed $parameters
596
+     * @param int $limit Number of items to return (optional) Returns all by default
597
+     * @param boolean $includeCollections
598
+     * @return mixed Return depends on format
599
+     */
600
+    public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
601
+                                            $limit = -1, $includeCollections = false) {
602
+        return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
603
+            $parameters, $limit, $includeCollections);
604
+    }
605
+
606
+    /**
607
+     * Get the shared item of item type owned by the current user
608
+     * @param string $itemType
609
+     * @param string $itemSource
610
+     * @param int $format (optional) Format type must be defined by the backend
611
+     * @param mixed $parameters
612
+     * @param boolean $includeCollections
613
+     * @return mixed Return depends on format
614
+     */
615
+    public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
616
+                                            $parameters = null, $includeCollections = false) {
617
+        return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
618
+            $parameters, -1, $includeCollections);
619
+    }
620
+
621
+    /**
622
+     * Get all users an item is shared with
623
+     * @param string $itemType
624
+     * @param string $itemSource
625
+     * @param string $uidOwner
626
+     * @param boolean $includeCollections
627
+     * @param boolean $checkExpireDate
628
+     * @return array Return array of users
629
+     */
630
+    public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false, $checkExpireDate = true) {
631
+
632
+        $users = array();
633
+        $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections, false, $checkExpireDate);
634
+        if ($items) {
635
+            foreach ($items as $item) {
636
+                if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
637
+                    $users[] = $item['share_with'];
638
+                } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
639
+
640
+                    $group = \OC::$server->getGroupManager()->get($item['share_with']);
641
+                    $userIds = [];
642
+                    if ($group) {
643
+                        $users = $group->searchUsers('', -1, 0);
644
+                        foreach ($users as $user) {
645
+                            $userIds[] = $user->getUID();
646
+                        }
647
+                        return $userIds;
648
+                    }
649
+
650
+                    $users = array_merge($users, $userIds);
651
+                }
652
+            }
653
+        }
654
+        return $users;
655
+    }
656
+
657
+    /**
658
+     * Share an item with a user, group, or via private link
659
+     * @param string $itemType
660
+     * @param string $itemSource
661
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
662
+     * @param string $shareWith User or group the item is being shared with
663
+     * @param int $permissions CRUDS
664
+     * @param string $itemSourceName
665
+     * @param \DateTime $expirationDate
666
+     * @param bool $passwordChanged
667
+     * @return boolean|string Returns true on success or false on failure, Returns token on success for links
668
+     * @throws \OC\HintException when the share type is remote and the shareWith is invalid
669
+     * @throws \Exception
670
+     */
671
+    public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
672
+
673
+        $backend = self::getBackend($itemType);
674
+        $l = \OC::$server->getL10N('lib');
675
+
676
+        if ($backend->isShareTypeAllowed($shareType) === false) {
677
+            $message = 'Sharing %s failed, because the backend does not allow shares from type %i';
678
+            $message_t = $l->t('Sharing %s failed, because the backend does not allow shares from type %i', array($itemSourceName, $shareType));
679
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareType), \OCP\Util::DEBUG);
680
+            throw new \Exception($message_t);
681
+        }
682
+
683
+        $uidOwner = \OC_User::getUser();
684
+        $shareWithinGroupOnly = self::shareWithGroupMembersOnly();
685
+
686
+        if (is_null($itemSourceName)) {
687
+            $itemSourceName = $itemSource;
688
+        }
689
+        $itemName = $itemSourceName;
690
+
691
+        // check if file can be shared
692
+        if ($itemType === 'file' or $itemType === 'folder') {
693
+            $path = \OC\Files\Filesystem::getPath($itemSource);
694
+            $itemName = $path;
695
+
696
+            // verify that the file exists before we try to share it
697
+            if (!$path) {
698
+                $message = 'Sharing %s failed, because the file does not exist';
699
+                $message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName));
700
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
701
+                throw new \Exception($message_t);
702
+            }
703
+            // verify that the user has share permission
704
+            if (!\OC\Files\Filesystem::isSharable($path) || \OCP\Util::isSharingDisabledForUser()) {
705
+                $message = 'You are not allowed to share %s';
706
+                $message_t = $l->t('You are not allowed to share %s', [$path]);
707
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
708
+                throw new \Exception($message_t);
709
+            }
710
+        }
711
+
712
+        //verify that we don't share a folder which already contains a share mount point
713
+        if ($itemType === 'folder') {
714
+            $path = '/' . $uidOwner . '/files' . \OC\Files\Filesystem::getPath($itemSource) . '/';
715
+            $mountManager = \OC\Files\Filesystem::getMountManager();
716
+            $mounts = $mountManager->findIn($path);
717
+            foreach ($mounts as $mount) {
718
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
719
+                    $message = 'Sharing "' . $itemSourceName . '" failed, because it contains files shared with you!';
720
+                    \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
721
+                    throw new \Exception($message);
722
+                }
723
+
724
+            }
725
+        }
726
+
727
+        // single file shares should never have delete permissions
728
+        if ($itemType === 'file') {
729
+            $permissions = (int)$permissions & ~\OCP\Constants::PERMISSION_DELETE;
730
+        }
731
+
732
+        //Validate expirationDate
733
+        if ($expirationDate !== null) {
734
+            try {
735
+                /*
736 736
 				 * Reuse the validateExpireDate.
737 737
 				 * We have to pass time() since the second arg is the time
738 738
 				 * the file was shared, since it is not shared yet we just use
739 739
 				 * the current time.
740 740
 				 */
741
-				$expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
742
-			} catch (\Exception $e) {
743
-				throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
744
-			}
745
-		}
746
-
747
-		// Verify share type and sharing conditions are met
748
-		if ($shareType === self::SHARE_TYPE_USER) {
749
-			if ($shareWith == $uidOwner) {
750
-				$message = 'Sharing %s failed, because you can not share with yourself';
751
-				$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
752
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
753
-				throw new \Exception($message_t);
754
-			}
755
-			if (!\OC_User::userExists($shareWith)) {
756
-				$message = 'Sharing %s failed, because the user %s does not exist';
757
-				$message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
758
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
759
-				throw new \Exception($message_t);
760
-			}
761
-			if ($shareWithinGroupOnly) {
762
-				$userManager = \OC::$server->getUserManager();
763
-				$groupManager = \OC::$server->getGroupManager();
764
-				$userOwner = $userManager->get($uidOwner);
765
-				$userShareWith = $userManager->get($shareWith);
766
-				$groupsOwner = [];
767
-				$groupsShareWith = [];
768
-				if ($userOwner) {
769
-					$groupsOwner = $groupManager->getUserGroupIds($userOwner);
770
-				}
771
-				if ($userShareWith) {
772
-					$groupsShareWith = $groupManager->getUserGroupIds($userShareWith);
773
-				}
774
-				$inGroup = array_intersect($groupsOwner, $groupsShareWith);
775
-				if (empty($inGroup)) {
776
-					$message = 'Sharing %s failed, because the user '
777
-						.'%s is not a member of any groups that %s is a member of';
778
-					$message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner));
779
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
780
-					throw new \Exception($message_t);
781
-				}
782
-			}
783
-			// Check if the item source is already shared with the user, either from the same owner or a different user
784
-			if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
785
-				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
786
-				// Only allow the same share to occur again if it is the same
787
-				// owner and is not a user share, this use case is for increasing
788
-				// permissions for a specific user
789
-				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
790
-					$message = 'Sharing %s failed, because this item is already shared with %s';
791
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
792
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
793
-					throw new \Exception($message_t);
794
-				}
795
-			}
796
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
797
-				$shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
798
-				// Only allow the same share to occur again if it is the same
799
-				// owner and is not a user share, this use case is for increasing
800
-				// permissions for a specific user
801
-				if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
802
-					$message = 'Sharing %s failed, because this item is already shared with user %s';
803
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith));
804
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
805
-					throw new \Exception($message_t);
806
-				}
807
-			}
808
-		} else if ($shareType === self::SHARE_TYPE_GROUP) {
809
-			if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
810
-				$message = 'Sharing %s failed, because the group %s does not exist';
811
-				$message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
812
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
813
-				throw new \Exception($message_t);
814
-			}
815
-			if ($shareWithinGroupOnly) {
816
-				$group = \OC::$server->getGroupManager()->get($shareWith);
817
-				$user = \OC::$server->getUserManager()->get($uidOwner);
818
-				if (!$group || !$user || !$group->inGroup($user)) {
819
-					$message = 'Sharing %s failed, because '
820
-						. '%s is not a member of the group %s';
821
-					$message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
822
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
823
-					throw new \Exception($message_t);
824
-				}
825
-			}
826
-			// Check if the item source is already shared with the group, either from the same owner or a different user
827
-			// The check for each user in the group is done inside the put() function
828
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
829
-				null, self::FORMAT_NONE, null, 1, true, true)) {
830
-
831
-				if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
832
-					$message = 'Sharing %s failed, because this item is already shared with %s';
833
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
834
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
835
-					throw new \Exception($message_t);
836
-				}
837
-			}
838
-			// Convert share with into an array with the keys group and users
839
-			$group = $shareWith;
840
-			$shareWith = array();
841
-			$shareWith['group'] = $group;
842
-
843
-
844
-			$groupObject = \OC::$server->getGroupManager()->get($group);
845
-			$userIds = [];
846
-			if ($groupObject) {
847
-				$users = $groupObject->searchUsers('', -1, 0);
848
-				foreach ($users as $user) {
849
-					$userIds[] = $user->getUID();
850
-				}
851
-			}
852
-
853
-			$shareWith['users'] = array_diff($userIds, array($uidOwner));
854
-		} else if ($shareType === self::SHARE_TYPE_LINK) {
855
-			$updateExistingShare = false;
856
-			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
857
-
858
-				// IF the password is changed via the old ajax endpoint verify it before deleting the old share
859
-				if ($passwordChanged === true) {
860
-					self::verifyPassword($shareWith);
861
-				}
862
-
863
-				// when updating a link share
864
-				// FIXME Don't delete link if we update it
865
-				if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
866
-					$uidOwner, self::FORMAT_NONE, null, 1)) {
867
-					// remember old token
868
-					$oldToken = $checkExists['token'];
869
-					$oldPermissions = $checkExists['permissions'];
870
-					//delete the old share
871
-					Helper::delete($checkExists['id']);
872
-					$updateExistingShare = true;
873
-				}
874
-
875
-				if ($passwordChanged === null) {
876
-					// Generate hash of password - same method as user passwords
877
-					if (is_string($shareWith) && $shareWith !== '') {
878
-						self::verifyPassword($shareWith);
879
-						$shareWith = \OC::$server->getHasher()->hash($shareWith);
880
-					} else {
881
-						// reuse the already set password, but only if we change permissions
882
-						// otherwise the user disabled the password protection
883
-						if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
884
-							$shareWith = $checkExists['share_with'];
885
-						}
886
-					}
887
-				} else {
888
-					if ($passwordChanged === true) {
889
-						if (is_string($shareWith) && $shareWith !== '') {
890
-							self::verifyPassword($shareWith);
891
-							$shareWith = \OC::$server->getHasher()->hash($shareWith);
892
-						}
893
-					} else if ($updateExistingShare) {
894
-						$shareWith = $checkExists['share_with'];
895
-					}
896
-				}
897
-
898
-				if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
899
-					$message = 'You need to provide a password to create a public link, only protected links are allowed';
900
-					$message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
901
-					\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
902
-					throw new \Exception($message_t);
903
-				}
904
-
905
-				if ($updateExistingShare === false &&
906
-					self::isDefaultExpireDateEnabled() &&
907
-					empty($expirationDate)) {
908
-					$expirationDate = Helper::calcExpireDate();
909
-				}
910
-
911
-				// Generate token
912
-				if (isset($oldToken)) {
913
-					$token = $oldToken;
914
-				} else {
915
-					$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH,
916
-						\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
917
-					);
918
-				}
919
-				$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
920
-					null, $token, $itemSourceName, $expirationDate);
921
-				if ($result) {
922
-					return $token;
923
-				} else {
924
-					return false;
925
-				}
926
-			}
927
-			$message = 'Sharing %s failed, because sharing with links is not allowed';
928
-			$message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName));
929
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
930
-			throw new \Exception($message_t);
931
-		} else if ($shareType === self::SHARE_TYPE_REMOTE) {
932
-
933
-			/*
741
+                $expirationDate = self::validateExpireDate($expirationDate->format('Y-m-d'), time(), $itemType, $itemSource);
742
+            } catch (\Exception $e) {
743
+                throw new \OC\HintException($e->getMessage(), $e->getMessage(), 404);
744
+            }
745
+        }
746
+
747
+        // Verify share type and sharing conditions are met
748
+        if ($shareType === self::SHARE_TYPE_USER) {
749
+            if ($shareWith == $uidOwner) {
750
+                $message = 'Sharing %s failed, because you can not share with yourself';
751
+                $message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
752
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
753
+                throw new \Exception($message_t);
754
+            }
755
+            if (!\OC_User::userExists($shareWith)) {
756
+                $message = 'Sharing %s failed, because the user %s does not exist';
757
+                $message_t = $l->t('Sharing %s failed, because the user %s does not exist', array($itemSourceName, $shareWith));
758
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
759
+                throw new \Exception($message_t);
760
+            }
761
+            if ($shareWithinGroupOnly) {
762
+                $userManager = \OC::$server->getUserManager();
763
+                $groupManager = \OC::$server->getGroupManager();
764
+                $userOwner = $userManager->get($uidOwner);
765
+                $userShareWith = $userManager->get($shareWith);
766
+                $groupsOwner = [];
767
+                $groupsShareWith = [];
768
+                if ($userOwner) {
769
+                    $groupsOwner = $groupManager->getUserGroupIds($userOwner);
770
+                }
771
+                if ($userShareWith) {
772
+                    $groupsShareWith = $groupManager->getUserGroupIds($userShareWith);
773
+                }
774
+                $inGroup = array_intersect($groupsOwner, $groupsShareWith);
775
+                if (empty($inGroup)) {
776
+                    $message = 'Sharing %s failed, because the user '
777
+                        .'%s is not a member of any groups that %s is a member of';
778
+                    $message_t = $l->t('Sharing %s failed, because the user %s is not a member of any groups that %s is a member of', array($itemName, $shareWith, $uidOwner));
779
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemName, $shareWith, $uidOwner), \OCP\Util::DEBUG);
780
+                    throw new \Exception($message_t);
781
+                }
782
+            }
783
+            // Check if the item source is already shared with the user, either from the same owner or a different user
784
+            if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
785
+                $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
786
+                // Only allow the same share to occur again if it is the same
787
+                // owner and is not a user share, this use case is for increasing
788
+                // permissions for a specific user
789
+                if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
790
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
791
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
792
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
793
+                    throw new \Exception($message_t);
794
+                }
795
+            }
796
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_USER,
797
+                $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
798
+                // Only allow the same share to occur again if it is the same
799
+                // owner and is not a user share, this use case is for increasing
800
+                // permissions for a specific user
801
+                if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
802
+                    $message = 'Sharing %s failed, because this item is already shared with user %s';
803
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with user %s', array($itemSourceName, $shareWith));
804
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::ERROR);
805
+                    throw new \Exception($message_t);
806
+                }
807
+            }
808
+        } else if ($shareType === self::SHARE_TYPE_GROUP) {
809
+            if (!\OC::$server->getGroupManager()->groupExists($shareWith)) {
810
+                $message = 'Sharing %s failed, because the group %s does not exist';
811
+                $message_t = $l->t('Sharing %s failed, because the group %s does not exist', array($itemSourceName, $shareWith));
812
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
813
+                throw new \Exception($message_t);
814
+            }
815
+            if ($shareWithinGroupOnly) {
816
+                $group = \OC::$server->getGroupManager()->get($shareWith);
817
+                $user = \OC::$server->getUserManager()->get($uidOwner);
818
+                if (!$group || !$user || !$group->inGroup($user)) {
819
+                    $message = 'Sharing %s failed, because '
820
+                        . '%s is not a member of the group %s';
821
+                    $message_t = $l->t('Sharing %s failed, because %s is not a member of the group %s', array($itemSourceName, $uidOwner, $shareWith));
822
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner, $shareWith), \OCP\Util::DEBUG);
823
+                    throw new \Exception($message_t);
824
+                }
825
+            }
826
+            // Check if the item source is already shared with the group, either from the same owner or a different user
827
+            // The check for each user in the group is done inside the put() function
828
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
829
+                null, self::FORMAT_NONE, null, 1, true, true)) {
830
+
831
+                if ($checkExists['share_with'] === $shareWith && $checkExists['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
832
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
833
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
834
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
835
+                    throw new \Exception($message_t);
836
+                }
837
+            }
838
+            // Convert share with into an array with the keys group and users
839
+            $group = $shareWith;
840
+            $shareWith = array();
841
+            $shareWith['group'] = $group;
842
+
843
+
844
+            $groupObject = \OC::$server->getGroupManager()->get($group);
845
+            $userIds = [];
846
+            if ($groupObject) {
847
+                $users = $groupObject->searchUsers('', -1, 0);
848
+                foreach ($users as $user) {
849
+                    $userIds[] = $user->getUID();
850
+                }
851
+            }
852
+
853
+            $shareWith['users'] = array_diff($userIds, array($uidOwner));
854
+        } else if ($shareType === self::SHARE_TYPE_LINK) {
855
+            $updateExistingShare = false;
856
+            if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
857
+
858
+                // IF the password is changed via the old ajax endpoint verify it before deleting the old share
859
+                if ($passwordChanged === true) {
860
+                    self::verifyPassword($shareWith);
861
+                }
862
+
863
+                // when updating a link share
864
+                // FIXME Don't delete link if we update it
865
+                if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
866
+                    $uidOwner, self::FORMAT_NONE, null, 1)) {
867
+                    // remember old token
868
+                    $oldToken = $checkExists['token'];
869
+                    $oldPermissions = $checkExists['permissions'];
870
+                    //delete the old share
871
+                    Helper::delete($checkExists['id']);
872
+                    $updateExistingShare = true;
873
+                }
874
+
875
+                if ($passwordChanged === null) {
876
+                    // Generate hash of password - same method as user passwords
877
+                    if (is_string($shareWith) && $shareWith !== '') {
878
+                        self::verifyPassword($shareWith);
879
+                        $shareWith = \OC::$server->getHasher()->hash($shareWith);
880
+                    } else {
881
+                        // reuse the already set password, but only if we change permissions
882
+                        // otherwise the user disabled the password protection
883
+                        if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
884
+                            $shareWith = $checkExists['share_with'];
885
+                        }
886
+                    }
887
+                } else {
888
+                    if ($passwordChanged === true) {
889
+                        if (is_string($shareWith) && $shareWith !== '') {
890
+                            self::verifyPassword($shareWith);
891
+                            $shareWith = \OC::$server->getHasher()->hash($shareWith);
892
+                        }
893
+                    } else if ($updateExistingShare) {
894
+                        $shareWith = $checkExists['share_with'];
895
+                    }
896
+                }
897
+
898
+                if (\OCP\Util::isPublicLinkPasswordRequired() && empty($shareWith)) {
899
+                    $message = 'You need to provide a password to create a public link, only protected links are allowed';
900
+                    $message_t = $l->t('You need to provide a password to create a public link, only protected links are allowed');
901
+                    \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
902
+                    throw new \Exception($message_t);
903
+                }
904
+
905
+                if ($updateExistingShare === false &&
906
+                    self::isDefaultExpireDateEnabled() &&
907
+                    empty($expirationDate)) {
908
+                    $expirationDate = Helper::calcExpireDate();
909
+                }
910
+
911
+                // Generate token
912
+                if (isset($oldToken)) {
913
+                    $token = $oldToken;
914
+                } else {
915
+                    $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH,
916
+                        \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
917
+                    );
918
+                }
919
+                $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
920
+                    null, $token, $itemSourceName, $expirationDate);
921
+                if ($result) {
922
+                    return $token;
923
+                } else {
924
+                    return false;
925
+                }
926
+            }
927
+            $message = 'Sharing %s failed, because sharing with links is not allowed';
928
+            $message_t = $l->t('Sharing %s failed, because sharing with links is not allowed', array($itemSourceName));
929
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
930
+            throw new \Exception($message_t);
931
+        } else if ($shareType === self::SHARE_TYPE_REMOTE) {
932
+
933
+            /*
934 934
 			 * Check if file is not already shared with the remote user
935 935
 			 */
936
-			if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
937
-				$shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
938
-					$message = 'Sharing %s failed, because this item is already shared with %s';
939
-					$message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
940
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
941
-					throw new \Exception($message_t);
942
-			}
943
-
944
-			// don't allow federated shares if source and target server are the same
945
-			list($user, $remote) = Helper::splitUserRemote($shareWith);
946
-			$currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
947
-			$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
948
-			if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
949
-				$message = 'Not allowed to create a federated share with the same user.';
950
-				$message_t = $l->t('Not allowed to create a federated share with the same user');
951
-				\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
952
-				throw new \Exception($message_t);
953
-			}
954
-
955
-			$token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
956
-				\OCP\Security\ISecureRandom::CHAR_DIGITS);
957
-
958
-			$shareWith = $user . '@' . $remote;
959
-			$shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
960
-
961
-			$send = false;
962
-			if ($shareId) {
963
-				$send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
964
-			}
965
-
966
-			if ($send === false) {
967
-				$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
968
-				self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
969
-				$message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith));
970
-				throw new \Exception($message_t);
971
-			}
972
-
973
-			return $send;
974
-		} else {
975
-			// Future share types need to include their own conditions
976
-			$message = 'Share type %s is not valid for %s';
977
-			$message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
978
-			\OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
979
-			throw new \Exception($message_t);
980
-		}
981
-
982
-		// Put the item into the database
983
-		$result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
984
-
985
-		return $result ? true : false;
986
-	}
987
-
988
-	/**
989
-	 * Unshare an item from a user, group, or delete a private link
990
-	 * @param string $itemType
991
-	 * @param string $itemSource
992
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
993
-	 * @param string $shareWith User or group the item is being shared with
994
-	 * @param string $owner owner of the share, if null the current user is used
995
-	 * @return boolean true on success or false on failure
996
-	 */
997
-	public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
998
-
999
-		// check if it is a valid itemType
1000
-		self::getBackend($itemType);
1001
-
1002
-		$items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
1003
-
1004
-		$toDelete = array();
1005
-		$newParent = null;
1006
-		$currentUser = $owner ? $owner : \OC_User::getUser();
1007
-		foreach ($items as $item) {
1008
-			// delete the item with the expected share_type and owner
1009
-			if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
1010
-				$toDelete = $item;
1011
-				// if there is more then one result we don't have to delete the children
1012
-				// but update their parent. For group shares the new parent should always be
1013
-				// the original group share and not the db entry with the unique name
1014
-			} else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
1015
-				$newParent = $item['parent'];
1016
-			} else {
1017
-				$newParent = $item['id'];
1018
-			}
1019
-		}
1020
-
1021
-		if (!empty($toDelete)) {
1022
-			self::unshareItem($toDelete, $newParent);
1023
-			return true;
1024
-		}
1025
-		return false;
1026
-	}
1027
-
1028
-	/**
1029
-	 * Unshare an item from all users, groups, and remove all links
1030
-	 * @param string $itemType
1031
-	 * @param string $itemSource
1032
-	 * @return boolean true on success or false on failure
1033
-	 */
1034
-	public static function unshareAll($itemType, $itemSource) {
1035
-		// Get all of the owners of shares of this item.
1036
-		$query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
1037
-		$result = $query->execute(array($itemType, $itemSource));
1038
-		$shares = array();
1039
-		// Add each owner's shares to the array of all shares for this item.
1040
-		while ($row = $result->fetchRow()) {
1041
-			$shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
1042
-		}
1043
-		if (!empty($shares)) {
1044
-			// Pass all the vars we have for now, they may be useful
1045
-			$hookParams = array(
1046
-				'itemType' => $itemType,
1047
-				'itemSource' => $itemSource,
1048
-				'shares' => $shares,
1049
-			);
1050
-			\OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
1051
-			foreach ($shares as $share) {
1052
-				self::unshareItem($share);
1053
-			}
1054
-			\OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
1055
-			return true;
1056
-		}
1057
-		return false;
1058
-	}
1059
-
1060
-	/**
1061
-	 * Unshare an item shared with the current user
1062
-	 * @param string $itemType
1063
-	 * @param string $itemOrigin Item target or source
1064
-	 * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
1065
-	 * @return boolean true on success or false on failure
1066
-	 *
1067
-	 * Unsharing from self is not allowed for items inside collections
1068
-	 */
1069
-	public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
1070
-		$originType = ($originIsSource) ? 'source' : 'target';
1071
-		$uid = \OCP\User::getUser();
1072
-
1073
-		if ($itemType === 'file' || $itemType === 'folder') {
1074
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1075
-		} else {
1076
-			$statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1077
-		}
1078
-
1079
-		$query = \OCP\DB::prepare($statement);
1080
-		$result = $query->execute(array($itemType, $itemOrigin));
1081
-
1082
-		$shares = $result->fetchAll();
1083
-
1084
-		$listOfUnsharedItems = array();
1085
-
1086
-		$itemUnshared = false;
1087
-		foreach ($shares as $share) {
1088
-			if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1089
-				$share['share_with'] === $uid) {
1090
-				$deletedShares = Helper::delete($share['id']);
1091
-				$shareTmp = array(
1092
-					'id' => $share['id'],
1093
-					'shareWith' => $share['share_with'],
1094
-					'itemTarget' => $share['item_target'],
1095
-					'itemType' => $share['item_type'],
1096
-					'shareType' => (int)$share['share_type'],
1097
-				);
1098
-				if (isset($share['file_target'])) {
1099
-					$shareTmp['fileTarget'] = $share['file_target'];
1100
-				}
1101
-				$listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
1102
-				$itemUnshared = true;
1103
-				break;
1104
-			} elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1105
-				$group = \OC::$server->getGroupManager()->get($share['share_with']);
1106
-				$user = \OC::$server->getUserManager()->get($uid);
1107
-				if ($group && $user && $group->inGroup($user)) {
1108
-					$groupShare = $share;
1109
-				}
1110
-			} elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1111
-				$share['share_with'] === $uid) {
1112
-				$uniqueGroupShare = $share;
1113
-			}
1114
-		}
1115
-
1116
-		if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
1117
-			$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
1118
-				.' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
1119
-				.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
1120
-				.' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
1121
-			$query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
1122
-				$groupShare['id'], self::$shareTypeGroupUserUnique,
1123
-				\OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
1124
-				$groupShare['file_target']));
1125
-			$shareTmp = array(
1126
-				'id' => $groupShare['id'],
1127
-				'shareWith' => $groupShare['share_with'],
1128
-				'itemTarget' => $groupShare['item_target'],
1129
-				'itemType' => $groupShare['item_type'],
1130
-				'shareType' => (int)$groupShare['share_type'],
1131
-			);
1132
-			if (isset($groupShare['file_target'])) {
1133
-				$shareTmp['fileTarget'] = $groupShare['file_target'];
1134
-			}
1135
-			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1136
-			$itemUnshared = true;
1137
-		} elseif (!$itemUnshared && isset($uniqueGroupShare)) {
1138
-			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1139
-			$query->execute(array(0, $uniqueGroupShare['id']));
1140
-			$shareTmp = array(
1141
-				'id' => $uniqueGroupShare['id'],
1142
-				'shareWith' => $uniqueGroupShare['share_with'],
1143
-				'itemTarget' => $uniqueGroupShare['item_target'],
1144
-				'itemType' => $uniqueGroupShare['item_type'],
1145
-				'shareType' => (int)$uniqueGroupShare['share_type'],
1146
-			);
1147
-			if (isset($uniqueGroupShare['file_target'])) {
1148
-				$shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
1149
-			}
1150
-			$listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1151
-			$itemUnshared = true;
1152
-		}
1153
-
1154
-		if ($itemUnshared) {
1155
-			\OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
1156
-				array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType));
1157
-		}
1158
-
1159
-		return $itemUnshared;
1160
-	}
1161
-
1162
-	/**
1163
-	 * sent status if users got informed by mail about share
1164
-	 * @param string $itemType
1165
-	 * @param string $itemSource
1166
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1167
-	 * @param string $recipient with whom was the file shared
1168
-	 * @param boolean $status
1169
-	 */
1170
-	public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
1171
-		$status = $status ? 1 : 0;
1172
-
1173
-		$query = \OC_DB::prepare(
1174
-			'UPDATE `*PREFIX*share`
936
+            if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_REMOTE,
937
+                $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true, true)) {
938
+                    $message = 'Sharing %s failed, because this item is already shared with %s';
939
+                    $message_t = $l->t('Sharing %s failed, because this item is already shared with %s', array($itemSourceName, $shareWith));
940
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
941
+                    throw new \Exception($message_t);
942
+            }
943
+
944
+            // don't allow federated shares if source and target server are the same
945
+            list($user, $remote) = Helper::splitUserRemote($shareWith);
946
+            $currentServer = self::removeProtocolFromUrl(\OC::$server->getURLGenerator()->getAbsoluteURL('/'));
947
+            $currentUser = \OC::$server->getUserSession()->getUser()->getUID();
948
+            if (Helper::isSameUserOnSameServer($user, $remote, $currentUser, $currentServer)) {
949
+                $message = 'Not allowed to create a federated share with the same user.';
950
+                $message_t = $l->t('Not allowed to create a federated share with the same user');
951
+                \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::DEBUG);
952
+                throw new \Exception($message_t);
953
+            }
954
+
955
+            $token = \OC::$server->getSecureRandom()->generate(self::TOKEN_LENGTH, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_UPPER .
956
+                \OCP\Security\ISecureRandom::CHAR_DIGITS);
957
+
958
+            $shareWith = $user . '@' . $remote;
959
+            $shareId = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token, $itemSourceName);
960
+
961
+            $send = false;
962
+            if ($shareId) {
963
+                $send = self::sendRemoteShare($token, $shareWith, $itemSourceName, $shareId, $uidOwner);
964
+            }
965
+
966
+            if ($send === false) {
967
+                $currentUser = \OC::$server->getUserSession()->getUser()->getUID();
968
+                self::unshare($itemType, $itemSource, $shareType, $shareWith, $currentUser);
969
+                $message_t = $l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable.', array($itemSourceName, $shareWith));
970
+                throw new \Exception($message_t);
971
+            }
972
+
973
+            return $send;
974
+        } else {
975
+            // Future share types need to include their own conditions
976
+            $message = 'Share type %s is not valid for %s';
977
+            $message_t = $l->t('Share type %s is not valid for %s', array($shareType, $itemSource));
978
+            \OCP\Util::writeLog('OCP\Share', sprintf($message, $shareType, $itemSource), \OCP\Util::DEBUG);
979
+            throw new \Exception($message_t);
980
+        }
981
+
982
+        // Put the item into the database
983
+        $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, null, $itemSourceName, $expirationDate);
984
+
985
+        return $result ? true : false;
986
+    }
987
+
988
+    /**
989
+     * Unshare an item from a user, group, or delete a private link
990
+     * @param string $itemType
991
+     * @param string $itemSource
992
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
993
+     * @param string $shareWith User or group the item is being shared with
994
+     * @param string $owner owner of the share, if null the current user is used
995
+     * @return boolean true on success or false on failure
996
+     */
997
+    public static function unshare($itemType, $itemSource, $shareType, $shareWith, $owner = null) {
998
+
999
+        // check if it is a valid itemType
1000
+        self::getBackend($itemType);
1001
+
1002
+        $items = self::getItemSharedWithUser($itemType, $itemSource, $shareWith, $owner, $shareType);
1003
+
1004
+        $toDelete = array();
1005
+        $newParent = null;
1006
+        $currentUser = $owner ? $owner : \OC_User::getUser();
1007
+        foreach ($items as $item) {
1008
+            // delete the item with the expected share_type and owner
1009
+            if ((int)$item['share_type'] === (int)$shareType && $item['uid_owner'] === $currentUser) {
1010
+                $toDelete = $item;
1011
+                // if there is more then one result we don't have to delete the children
1012
+                // but update their parent. For group shares the new parent should always be
1013
+                // the original group share and not the db entry with the unique name
1014
+            } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
1015
+                $newParent = $item['parent'];
1016
+            } else {
1017
+                $newParent = $item['id'];
1018
+            }
1019
+        }
1020
+
1021
+        if (!empty($toDelete)) {
1022
+            self::unshareItem($toDelete, $newParent);
1023
+            return true;
1024
+        }
1025
+        return false;
1026
+    }
1027
+
1028
+    /**
1029
+     * Unshare an item from all users, groups, and remove all links
1030
+     * @param string $itemType
1031
+     * @param string $itemSource
1032
+     * @return boolean true on success or false on failure
1033
+     */
1034
+    public static function unshareAll($itemType, $itemSource) {
1035
+        // Get all of the owners of shares of this item.
1036
+        $query = \OC_DB::prepare( 'SELECT `uid_owner` from `*PREFIX*share` WHERE `item_type`=? AND `item_source`=?' );
1037
+        $result = $query->execute(array($itemType, $itemSource));
1038
+        $shares = array();
1039
+        // Add each owner's shares to the array of all shares for this item.
1040
+        while ($row = $result->fetchRow()) {
1041
+            $shares = array_merge($shares, self::getItems($itemType, $itemSource, null, null, $row['uid_owner']));
1042
+        }
1043
+        if (!empty($shares)) {
1044
+            // Pass all the vars we have for now, they may be useful
1045
+            $hookParams = array(
1046
+                'itemType' => $itemType,
1047
+                'itemSource' => $itemSource,
1048
+                'shares' => $shares,
1049
+            );
1050
+            \OC_Hook::emit('OCP\Share', 'pre_unshareAll', $hookParams);
1051
+            foreach ($shares as $share) {
1052
+                self::unshareItem($share);
1053
+            }
1054
+            \OC_Hook::emit('OCP\Share', 'post_unshareAll', $hookParams);
1055
+            return true;
1056
+        }
1057
+        return false;
1058
+    }
1059
+
1060
+    /**
1061
+     * Unshare an item shared with the current user
1062
+     * @param string $itemType
1063
+     * @param string $itemOrigin Item target or source
1064
+     * @param boolean $originIsSource true if $itemOrigin is the source, false if $itemOrigin is the target (optional)
1065
+     * @return boolean true on success or false on failure
1066
+     *
1067
+     * Unsharing from self is not allowed for items inside collections
1068
+     */
1069
+    public static function unshareFromSelf($itemType, $itemOrigin, $originIsSource = false) {
1070
+        $originType = ($originIsSource) ? 'source' : 'target';
1071
+        $uid = \OCP\User::getUser();
1072
+
1073
+        if ($itemType === 'file' || $itemType === 'folder') {
1074
+            $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `file_' . $originType . '` = ?';
1075
+        } else {
1076
+            $statement = 'SELECT * FROM `*PREFIX*share` WHERE `item_type` = ? and `item_' . $originType . '` = ?';
1077
+        }
1078
+
1079
+        $query = \OCP\DB::prepare($statement);
1080
+        $result = $query->execute(array($itemType, $itemOrigin));
1081
+
1082
+        $shares = $result->fetchAll();
1083
+
1084
+        $listOfUnsharedItems = array();
1085
+
1086
+        $itemUnshared = false;
1087
+        foreach ($shares as $share) {
1088
+            if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER &&
1089
+                $share['share_with'] === $uid) {
1090
+                $deletedShares = Helper::delete($share['id']);
1091
+                $shareTmp = array(
1092
+                    'id' => $share['id'],
1093
+                    'shareWith' => $share['share_with'],
1094
+                    'itemTarget' => $share['item_target'],
1095
+                    'itemType' => $share['item_type'],
1096
+                    'shareType' => (int)$share['share_type'],
1097
+                );
1098
+                if (isset($share['file_target'])) {
1099
+                    $shareTmp['fileTarget'] = $share['file_target'];
1100
+                }
1101
+                $listOfUnsharedItems = array_merge($listOfUnsharedItems, $deletedShares, array($shareTmp));
1102
+                $itemUnshared = true;
1103
+                break;
1104
+            } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) {
1105
+                $group = \OC::$server->getGroupManager()->get($share['share_with']);
1106
+                $user = \OC::$server->getUserManager()->get($uid);
1107
+                if ($group && $user && $group->inGroup($user)) {
1108
+                    $groupShare = $share;
1109
+                }
1110
+            } elseif ((int)$share['share_type'] === self::$shareTypeGroupUserUnique &&
1111
+                $share['share_with'] === $uid) {
1112
+                $uniqueGroupShare = $share;
1113
+            }
1114
+        }
1115
+
1116
+        if (!$itemUnshared && isset($groupShare) && !isset($uniqueGroupShare)) {
1117
+            $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
1118
+                .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
1119
+                .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
1120
+                .' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
1121
+            $query->execute(array($groupShare['item_type'], $groupShare['item_source'], $groupShare['item_target'],
1122
+                $groupShare['id'], self::$shareTypeGroupUserUnique,
1123
+                \OC_User::getUser(), $groupShare['uid_owner'], 0, $groupShare['stime'], $groupShare['file_source'],
1124
+                $groupShare['file_target']));
1125
+            $shareTmp = array(
1126
+                'id' => $groupShare['id'],
1127
+                'shareWith' => $groupShare['share_with'],
1128
+                'itemTarget' => $groupShare['item_target'],
1129
+                'itemType' => $groupShare['item_type'],
1130
+                'shareType' => (int)$groupShare['share_type'],
1131
+            );
1132
+            if (isset($groupShare['file_target'])) {
1133
+                $shareTmp['fileTarget'] = $groupShare['file_target'];
1134
+            }
1135
+            $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1136
+            $itemUnshared = true;
1137
+        } elseif (!$itemUnshared && isset($uniqueGroupShare)) {
1138
+            $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
1139
+            $query->execute(array(0, $uniqueGroupShare['id']));
1140
+            $shareTmp = array(
1141
+                'id' => $uniqueGroupShare['id'],
1142
+                'shareWith' => $uniqueGroupShare['share_with'],
1143
+                'itemTarget' => $uniqueGroupShare['item_target'],
1144
+                'itemType' => $uniqueGroupShare['item_type'],
1145
+                'shareType' => (int)$uniqueGroupShare['share_type'],
1146
+            );
1147
+            if (isset($uniqueGroupShare['file_target'])) {
1148
+                $shareTmp['fileTarget'] = $uniqueGroupShare['file_target'];
1149
+            }
1150
+            $listOfUnsharedItems = array_merge($listOfUnsharedItems, [$shareTmp]);
1151
+            $itemUnshared = true;
1152
+        }
1153
+
1154
+        if ($itemUnshared) {
1155
+            \OC_Hook::emit('OCP\Share', 'post_unshareFromSelf',
1156
+                array('unsharedItems' => $listOfUnsharedItems, 'itemType' => $itemType));
1157
+        }
1158
+
1159
+        return $itemUnshared;
1160
+    }
1161
+
1162
+    /**
1163
+     * sent status if users got informed by mail about share
1164
+     * @param string $itemType
1165
+     * @param string $itemSource
1166
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1167
+     * @param string $recipient with whom was the file shared
1168
+     * @param boolean $status
1169
+     */
1170
+    public static function setSendMailStatus($itemType, $itemSource, $shareType, $recipient, $status) {
1171
+        $status = $status ? 1 : 0;
1172
+
1173
+        $query = \OC_DB::prepare(
1174
+            'UPDATE `*PREFIX*share`
1175 1175
 					SET `mail_send` = ?
1176 1176
 					WHERE `item_type` = ? AND `item_source` = ? AND `share_type` = ? AND `share_with` = ?');
1177 1177
 
1178
-		$result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
1179
-
1180
-		if($result === false) {
1181
-			\OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1182
-		}
1183
-	}
1184
-
1185
-	/**
1186
-	 * Set the permissions of an item for a specific user or group
1187
-	 * @param string $itemType
1188
-	 * @param string $itemSource
1189
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1190
-	 * @param string $shareWith User or group the item is being shared with
1191
-	 * @param int $permissions CRUDS permissions
1192
-	 * @return boolean true on success or false on failure
1193
-	 * @throws \Exception when trying to grant more permissions then the user has himself
1194
-	 */
1195
-	public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
1196
-		$l = \OC::$server->getL10N('lib');
1197
-		$connection = \OC::$server->getDatabaseConnection();
1198
-
1199
-		$intArrayToLiteralArray = function($intArray, $eb) {
1200
-			return array_map(function($int) use ($eb) {
1201
-				return $eb->literal((int)$int, 'integer');
1202
-			}, $intArray);
1203
-		};
1204
-		$sanitizeItem = function($item) {
1205
-			$item['id'] = (int)$item['id'];
1206
-			$item['premissions'] = (int)$item['permissions'];
1207
-			return $item;
1208
-		};
1209
-
1210
-		if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
1211
-			\OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
1212
-			// Check if this item is a reshare and verify that the permissions
1213
-			// granted don't exceed the parent shared item
1214
-			if (isset($rootItem['parent'])) {
1215
-				$qb = $connection->getQueryBuilder();
1216
-				$qb->select('permissions')
1217
-					->from('share')
1218
-					->where($qb->expr()->eq('id', $qb->createParameter('id')))
1219
-					->setParameter(':id', $rootItem['parent']);
1220
-				$dbresult = $qb->execute();
1221
-
1222
-				$result = $dbresult->fetch();
1223
-				$dbresult->closeCursor();
1224
-				if (~(int)$result['permissions'] & $permissions) {
1225
-					$message = 'Setting permissions for %s failed,'
1226
-						.' because the permissions exceed permissions granted to %s';
1227
-					$message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
1228
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
1229
-					throw new \Exception($message_t);
1230
-				}
1231
-			}
1232
-			$qb = $connection->getQueryBuilder();
1233
-			$qb->update('share')
1234
-				->set('permissions', $qb->createParameter('permissions'))
1235
-				->where($qb->expr()->eq('id', $qb->createParameter('id')))
1236
-				->setParameter(':id', $rootItem['id'])
1237
-				->setParameter(':permissions', $permissions);
1238
-			$qb->execute();
1239
-			if ($itemType === 'file' || $itemType === 'folder') {
1240
-				\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
1241
-					'itemType' => $itemType,
1242
-					'itemSource' => $itemSource,
1243
-					'shareType' => $shareType,
1244
-					'shareWith' => $shareWith,
1245
-					'uidOwner' => \OC_User::getUser(),
1246
-					'permissions' => $permissions,
1247
-					'path' => $rootItem['path'],
1248
-					'share' => $rootItem
1249
-				));
1250
-			}
1251
-
1252
-			// Share id's to update with the new permissions
1253
-			$ids = [];
1254
-			$items = [];
1255
-
1256
-			// Check if permissions were removed
1257
-			if ((int)$rootItem['permissions'] & ~$permissions) {
1258
-				// If share permission is removed all reshares must be deleted
1259
-				if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1260
-					// delete all shares, keep parent and group children
1261
-					Helper::delete($rootItem['id'], true, null, null, true);
1262
-				}
1263
-
1264
-				// Remove permission from all children
1265
-				$parents = [$rootItem['id']];
1266
-				while (!empty($parents)) {
1267
-					$parents = $intArrayToLiteralArray($parents, $qb->expr());
1268
-					$qb = $connection->getQueryBuilder();
1269
-					$qb->select('id', 'permissions', 'item_type')
1270
-						->from('share')
1271
-						->where($qb->expr()->in('parent', $parents));
1272
-					$result = $qb->execute();
1273
-					// Reset parents array, only go through loop again if
1274
-					// items are found that need permissions removed
1275
-					$parents = [];
1276
-					while ($item = $result->fetch()) {
1277
-						$item = $sanitizeItem($item);
1278
-
1279
-						$items[] = $item;
1280
-						// Check if permissions need to be removed
1281
-						if ($item['permissions'] & ~$permissions) {
1282
-							// Add to list of items that need permissions removed
1283
-							$ids[] = $item['id'];
1284
-							$parents[] = $item['id'];
1285
-						}
1286
-					}
1287
-					$result->closeCursor();
1288
-				}
1289
-
1290
-				// Remove the permissions for all reshares of this item
1291
-				if (!empty($ids)) {
1292
-					$ids = "'".implode("','", $ids)."'";
1293
-					// TODO this should be done with Doctrine platform objects
1294
-					if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
1295
-						$andOp = 'BITAND(`permissions`, ?)';
1296
-					} else {
1297
-						$andOp = '`permissions` & ?';
1298
-					}
1299
-					$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1300
-						.' WHERE `id` IN ('.$ids.')');
1301
-					$query->execute(array($permissions));
1302
-				}
1303
-
1304
-			}
1305
-
1306
-			/*
1178
+        $result = $query->execute(array($status, $itemType, $itemSource, $shareType, $recipient));
1179
+
1180
+        if($result === false) {
1181
+            \OCP\Util::writeLog('OCP\Share', 'Couldn\'t set send mail status', \OCP\Util::ERROR);
1182
+        }
1183
+    }
1184
+
1185
+    /**
1186
+     * Set the permissions of an item for a specific user or group
1187
+     * @param string $itemType
1188
+     * @param string $itemSource
1189
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
1190
+     * @param string $shareWith User or group the item is being shared with
1191
+     * @param int $permissions CRUDS permissions
1192
+     * @return boolean true on success or false on failure
1193
+     * @throws \Exception when trying to grant more permissions then the user has himself
1194
+     */
1195
+    public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
1196
+        $l = \OC::$server->getL10N('lib');
1197
+        $connection = \OC::$server->getDatabaseConnection();
1198
+
1199
+        $intArrayToLiteralArray = function($intArray, $eb) {
1200
+            return array_map(function($int) use ($eb) {
1201
+                return $eb->literal((int)$int, 'integer');
1202
+            }, $intArray);
1203
+        };
1204
+        $sanitizeItem = function($item) {
1205
+            $item['id'] = (int)$item['id'];
1206
+            $item['premissions'] = (int)$item['permissions'];
1207
+            return $item;
1208
+        };
1209
+
1210
+        if ($rootItem = self::getItems($itemType, $itemSource, $shareType, $shareWith,
1211
+            \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
1212
+            // Check if this item is a reshare and verify that the permissions
1213
+            // granted don't exceed the parent shared item
1214
+            if (isset($rootItem['parent'])) {
1215
+                $qb = $connection->getQueryBuilder();
1216
+                $qb->select('permissions')
1217
+                    ->from('share')
1218
+                    ->where($qb->expr()->eq('id', $qb->createParameter('id')))
1219
+                    ->setParameter(':id', $rootItem['parent']);
1220
+                $dbresult = $qb->execute();
1221
+
1222
+                $result = $dbresult->fetch();
1223
+                $dbresult->closeCursor();
1224
+                if (~(int)$result['permissions'] & $permissions) {
1225
+                    $message = 'Setting permissions for %s failed,'
1226
+                        .' because the permissions exceed permissions granted to %s';
1227
+                    $message_t = $l->t('Setting permissions for %s failed, because the permissions exceed permissions granted to %s', array($itemSource, \OC_User::getUser()));
1228
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, \OC_User::getUser()), \OCP\Util::DEBUG);
1229
+                    throw new \Exception($message_t);
1230
+                }
1231
+            }
1232
+            $qb = $connection->getQueryBuilder();
1233
+            $qb->update('share')
1234
+                ->set('permissions', $qb->createParameter('permissions'))
1235
+                ->where($qb->expr()->eq('id', $qb->createParameter('id')))
1236
+                ->setParameter(':id', $rootItem['id'])
1237
+                ->setParameter(':permissions', $permissions);
1238
+            $qb->execute();
1239
+            if ($itemType === 'file' || $itemType === 'folder') {
1240
+                \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
1241
+                    'itemType' => $itemType,
1242
+                    'itemSource' => $itemSource,
1243
+                    'shareType' => $shareType,
1244
+                    'shareWith' => $shareWith,
1245
+                    'uidOwner' => \OC_User::getUser(),
1246
+                    'permissions' => $permissions,
1247
+                    'path' => $rootItem['path'],
1248
+                    'share' => $rootItem
1249
+                ));
1250
+            }
1251
+
1252
+            // Share id's to update with the new permissions
1253
+            $ids = [];
1254
+            $items = [];
1255
+
1256
+            // Check if permissions were removed
1257
+            if ((int)$rootItem['permissions'] & ~$permissions) {
1258
+                // If share permission is removed all reshares must be deleted
1259
+                if (($rootItem['permissions'] & \OCP\Constants::PERMISSION_SHARE) && (~$permissions & \OCP\Constants::PERMISSION_SHARE)) {
1260
+                    // delete all shares, keep parent and group children
1261
+                    Helper::delete($rootItem['id'], true, null, null, true);
1262
+                }
1263
+
1264
+                // Remove permission from all children
1265
+                $parents = [$rootItem['id']];
1266
+                while (!empty($parents)) {
1267
+                    $parents = $intArrayToLiteralArray($parents, $qb->expr());
1268
+                    $qb = $connection->getQueryBuilder();
1269
+                    $qb->select('id', 'permissions', 'item_type')
1270
+                        ->from('share')
1271
+                        ->where($qb->expr()->in('parent', $parents));
1272
+                    $result = $qb->execute();
1273
+                    // Reset parents array, only go through loop again if
1274
+                    // items are found that need permissions removed
1275
+                    $parents = [];
1276
+                    while ($item = $result->fetch()) {
1277
+                        $item = $sanitizeItem($item);
1278
+
1279
+                        $items[] = $item;
1280
+                        // Check if permissions need to be removed
1281
+                        if ($item['permissions'] & ~$permissions) {
1282
+                            // Add to list of items that need permissions removed
1283
+                            $ids[] = $item['id'];
1284
+                            $parents[] = $item['id'];
1285
+                        }
1286
+                    }
1287
+                    $result->closeCursor();
1288
+                }
1289
+
1290
+                // Remove the permissions for all reshares of this item
1291
+                if (!empty($ids)) {
1292
+                    $ids = "'".implode("','", $ids)."'";
1293
+                    // TODO this should be done with Doctrine platform objects
1294
+                    if (\OC::$server->getConfig()->getSystemValue("dbtype") === 'oci') {
1295
+                        $andOp = 'BITAND(`permissions`, ?)';
1296
+                    } else {
1297
+                        $andOp = '`permissions` & ?';
1298
+                    }
1299
+                    $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
1300
+                        .' WHERE `id` IN ('.$ids.')');
1301
+                    $query->execute(array($permissions));
1302
+                }
1303
+
1304
+            }
1305
+
1306
+            /*
1307 1307
 			 * Permissions were added
1308 1308
 			 * Update all USERGROUP shares. (So group shares where the user moved their mountpoint).
1309 1309
 			 */
1310
-			if ($permissions & ~(int)$rootItem['permissions']) {
1311
-				$qb = $connection->getQueryBuilder();
1312
-				$qb->select('id', 'permissions', 'item_type')
1313
-					->from('share')
1314
-					->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1315
-					->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1316
-					->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1317
-					->setParameter(':parent', (int)$rootItem['id'])
1318
-					->setParameter(':share_type', 2)
1319
-					->setParameter(':shareDeleted', 0);
1320
-				$result = $qb->execute();
1321
-
1322
-				$ids = [];
1323
-				while ($item = $result->fetch()) {
1324
-					$item = $sanitizeItem($item);
1325
-					$items[] = $item;
1326
-					$ids[] = $item['id'];
1327
-				}
1328
-				$result->closeCursor();
1329
-
1330
-				// Add permssions for all USERGROUP shares of this item
1331
-				if (!empty($ids)) {
1332
-					$ids = $intArrayToLiteralArray($ids, $qb->expr());
1333
-
1334
-					$qb = $connection->getQueryBuilder();
1335
-					$qb->update('share')
1336
-						->set('permissions', $qb->createParameter('permissions'))
1337
-						->where($qb->expr()->in('id', $ids))
1338
-						->setParameter(':permissions', $permissions);
1339
-					$qb->execute();
1340
-				}
1341
-			}
1342
-
1343
-			foreach ($items as $item) {
1344
-				\OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
1345
-			}
1346
-
1347
-			return true;
1348
-		}
1349
-		$message = 'Setting permissions for %s failed, because the item was not found';
1350
-		$message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
1351
-
1352
-		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
1353
-		throw new \Exception($message_t);
1354
-	}
1355
-
1356
-	/**
1357
-	 * validate expiration date if it meets all constraints
1358
-	 *
1359
-	 * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
1360
-	 * @param string $shareTime timestamp when the file was shared
1361
-	 * @param string $itemType
1362
-	 * @param string $itemSource
1363
-	 * @return \DateTime validated date
1364
-	 * @throws \Exception when the expire date is in the past or further in the future then the enforced date
1365
-	 */
1366
-	private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1367
-		$l = \OC::$server->getL10N('lib');
1368
-		$date = new \DateTime($expireDate);
1369
-		$today = new \DateTime('now');
1370
-
1371
-		// if the user doesn't provide a share time we need to get it from the database
1372
-		// fall-back mode to keep API stable, because the $shareTime parameter was added later
1373
-		$defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1374
-		if ($defaultExpireDateEnforced && $shareTime === null) {
1375
-			$items = self::getItemShared($itemType, $itemSource);
1376
-			$firstItem = reset($items);
1377
-			$shareTime = (int)$firstItem['stime'];
1378
-		}
1379
-
1380
-		if ($defaultExpireDateEnforced) {
1381
-			// initialize max date with share time
1382
-			$maxDate = new \DateTime();
1383
-			$maxDate->setTimestamp($shareTime);
1384
-			$maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1385
-			$maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1386
-			if ($date > $maxDate) {
1387
-				$warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1388
-				$warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1389
-				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1390
-				throw new \Exception($warning_t);
1391
-			}
1392
-		}
1393
-
1394
-		if ($date < $today) {
1395
-			$message = 'Cannot set expiration date. Expiration date is in the past';
1396
-			$message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1397
-			\OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1398
-			throw new \Exception($message_t);
1399
-		}
1400
-
1401
-		return $date;
1402
-	}
1403
-
1404
-	/**
1405
-	 * Set expiration date for a share
1406
-	 * @param string $itemType
1407
-	 * @param string $itemSource
1408
-	 * @param string $date expiration date
1409
-	 * @param int $shareTime timestamp from when the file was shared
1410
-	 * @return boolean
1411
-	 * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date
1412
-	 */
1413
-	public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1414
-		$user = \OC_User::getUser();
1415
-		$l = \OC::$server->getL10N('lib');
1416
-
1417
-		if ($date == '') {
1418
-			if (\OCP\Util::isDefaultExpireDateEnforced()) {
1419
-				$warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1420
-				$warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1421
-				\OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1422
-				throw new \Exception($warning_t);
1423
-			} else {
1424
-				$date = null;
1425
-			}
1426
-		} else {
1427
-			$date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1428
-		}
1429
-		$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1430
-		$query->bindValue(1, $date, 'datetime');
1431
-		$query->bindValue(2, $itemType);
1432
-		$query->bindValue(3, $itemSource);
1433
-		$query->bindValue(4, $user);
1434
-		$query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1435
-
1436
-		$query->execute();
1437
-
1438
-		\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
1439
-			'itemType' => $itemType,
1440
-			'itemSource' => $itemSource,
1441
-			'date' => $date,
1442
-			'uidOwner' => $user
1443
-		));
1444
-
1445
-		return true;
1446
-	}
1447
-
1448
-	/**
1449
-	 * Retrieve the owner of a connection
1450
-	 *
1451
-	 * @param IDBConnection $connection
1452
-	 * @param int $shareId
1453
-	 * @throws \Exception
1454
-	 * @return string uid of share owner
1455
-	 */
1456
-	private static function getShareOwner(IDBConnection $connection, $shareId) {
1457
-		$qb = $connection->getQueryBuilder();
1458
-
1459
-		$qb->select('uid_owner')
1460
-			->from('share')
1461
-			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1462
-			->setParameter(':shareId', $shareId);
1463
-		$result = $qb->execute();
1464
-		$result = $result->fetch();
1465
-
1466
-		if (empty($result)) {
1467
-			throw new \Exception('Share not found');
1468
-		}
1469
-
1470
-		return $result['uid_owner'];
1471
-	}
1472
-
1473
-	/**
1474
-	 * Set password for a public link share
1475
-	 *
1476
-	 * @param IUserSession $userSession
1477
-	 * @param IDBConnection $connection
1478
-	 * @param IConfig $config
1479
-	 * @param int $shareId
1480
-	 * @param string $password
1481
-	 * @throws \Exception
1482
-	 * @return boolean
1483
-	 */
1484
-	public static function setPassword(IUserSession $userSession,
1485
-	                                   IDBConnection $connection,
1486
-	                                   IConfig $config,
1487
-	                                   $shareId, $password) {
1488
-		$user = $userSession->getUser();
1489
-		if (is_null($user)) {
1490
-			throw new \Exception("User not logged in");
1491
-		}
1492
-
1493
-		$uid = self::getShareOwner($connection, $shareId);
1494
-
1495
-		if ($uid !== $user->getUID()) {
1496
-			throw new \Exception('Cannot update share of a different user');
1497
-		}
1498
-
1499
-		if ($password === '') {
1500
-			$password = null;
1501
-		}
1502
-
1503
-		//If passwords are enforced the password can't be null
1504
-		if (self::enforcePassword($config) && is_null($password)) {
1505
-			throw new \Exception('Cannot remove password');
1506
-		}
1507
-
1508
-		self::verifyPassword($password);
1509
-
1510
-		$qb = $connection->getQueryBuilder();
1511
-		$qb->update('share')
1512
-			->set('share_with', $qb->createParameter('pass'))
1513
-			->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1514
-			->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1515
-			->setParameter(':shareId', $shareId);
1516
-
1517
-		$qb->execute();
1518
-
1519
-		return true;
1520
-	}
1521
-
1522
-	/**
1523
-	 * Checks whether a share has expired, calls unshareItem() if yes.
1524
-	 * @param array $item Share data (usually database row)
1525
-	 * @return boolean True if item was expired, false otherwise.
1526
-	 */
1527
-	protected static function expireItem(array $item) {
1528
-
1529
-		$result = false;
1530
-
1531
-		// only use default expiration date for link shares
1532
-		if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1533
-
1534
-			// calculate expiration date
1535
-			if (!empty($item['expiration'])) {
1536
-				$userDefinedExpire = new \DateTime($item['expiration']);
1537
-				$expires = $userDefinedExpire->getTimestamp();
1538
-			} else {
1539
-				$expires = null;
1540
-			}
1541
-
1542
-
1543
-			// get default expiration settings
1544
-			$defaultSettings = Helper::getDefaultExpireSetting();
1545
-			$expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1546
-
1547
-
1548
-			if (is_int($expires)) {
1549
-				$now = time();
1550
-				if ($now > $expires) {
1551
-					self::unshareItem($item);
1552
-					$result = true;
1553
-				}
1554
-			}
1555
-		}
1556
-		return $result;
1557
-	}
1558
-
1559
-	/**
1560
-	 * Unshares a share given a share data array
1561
-	 * @param array $item Share data (usually database row)
1562
-	 * @param int $newParent parent ID
1563
-	 * @return null
1564
-	 */
1565
-	protected static function unshareItem(array $item, $newParent = null) {
1566
-
1567
-		$shareType = (int)$item['share_type'];
1568
-		$shareWith = null;
1569
-		if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1570
-			$shareWith = $item['share_with'];
1571
-		}
1572
-
1573
-		// Pass all the vars we have for now, they may be useful
1574
-		$hookParams = array(
1575
-			'id'            => $item['id'],
1576
-			'itemType'      => $item['item_type'],
1577
-			'itemSource'    => $item['item_source'],
1578
-			'shareType'     => $shareType,
1579
-			'shareWith'     => $shareWith,
1580
-			'itemParent'    => $item['parent'],
1581
-			'uidOwner'      => $item['uid_owner'],
1582
-		);
1583
-		if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1584
-			$hookParams['fileSource'] = $item['file_source'];
1585
-			$hookParams['fileTarget'] = $item['file_target'];
1586
-		}
1587
-
1588
-		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1589
-		$deletedShares = Helper::delete($item['id'], false, null, $newParent);
1590
-		$deletedShares[] = $hookParams;
1591
-		$hookParams['deletedShares'] = $deletedShares;
1592
-		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1593
-		if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1594
-			list(, $remote) = Helper::splitUserRemote($item['share_with']);
1595
-			self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1596
-		}
1597
-	}
1598
-
1599
-	/**
1600
-	 * Get the backend class for the specified item type
1601
-	 * @param string $itemType
1602
-	 * @throws \Exception
1603
-	 * @return \OCP\Share_Backend
1604
-	 */
1605
-	public static function getBackend($itemType) {
1606
-		$l = \OC::$server->getL10N('lib');
1607
-		if (isset(self::$backends[$itemType])) {
1608
-			return self::$backends[$itemType];
1609
-		} else if (isset(self::$backendTypes[$itemType]['class'])) {
1610
-			$class = self::$backendTypes[$itemType]['class'];
1611
-			if (class_exists($class)) {
1612
-				self::$backends[$itemType] = new $class;
1613
-				if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
1614
-					$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1615
-					$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
1616
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1617
-					throw new \Exception($message_t);
1618
-				}
1619
-				return self::$backends[$itemType];
1620
-			} else {
1621
-				$message = 'Sharing backend %s not found';
1622
-				$message_t = $l->t('Sharing backend %s not found', array($class));
1623
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1624
-				throw new \Exception($message_t);
1625
-			}
1626
-		}
1627
-		$message = 'Sharing backend for %s not found';
1628
-		$message_t = $l->t('Sharing backend for %s not found', array($itemType));
1629
-		\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR);
1630
-		throw new \Exception($message_t);
1631
-	}
1632
-
1633
-	/**
1634
-	 * Check if resharing is allowed
1635
-	 * @return boolean true if allowed or false
1636
-	 *
1637
-	 * Resharing is allowed by default if not configured
1638
-	 */
1639
-	public static function isResharingAllowed() {
1640
-		if (!isset(self::$isResharingAllowed)) {
1641
-			if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1642
-				self::$isResharingAllowed = true;
1643
-			} else {
1644
-				self::$isResharingAllowed = false;
1645
-			}
1646
-		}
1647
-		return self::$isResharingAllowed;
1648
-	}
1649
-
1650
-	/**
1651
-	 * Get a list of collection item types for the specified item type
1652
-	 * @param string $itemType
1653
-	 * @return array
1654
-	 */
1655
-	private static function getCollectionItemTypes($itemType) {
1656
-		$collectionTypes = array($itemType);
1657
-		foreach (self::$backendTypes as $type => $backend) {
1658
-			if (in_array($backend['collectionOf'], $collectionTypes)) {
1659
-				$collectionTypes[] = $type;
1660
-			}
1661
-		}
1662
-		// TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1663
-		if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1664
-			unset($collectionTypes[0]);
1665
-		}
1666
-		// Return array if collections were found or the item type is a
1667
-		// collection itself - collections can be inside collections
1668
-		if (count($collectionTypes) > 0) {
1669
-			return $collectionTypes;
1670
-		}
1671
-		return false;
1672
-	}
1673
-
1674
-	/**
1675
-	 * Get the owners of items shared with a user.
1676
-	 *
1677
-	 * @param string $user The user the items are shared with.
1678
-	 * @param string $type The type of the items shared with the user.
1679
-	 * @param boolean $includeCollections Include collection item types (optional)
1680
-	 * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1681
-	 * @return array
1682
-	 */
1683
-	public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1684
-		// First, we find out if $type is part of a collection (and if that collection is part of
1685
-		// another one and so on).
1686
-		$collectionTypes = array();
1687
-		if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1688
-			$collectionTypes[] = $type;
1689
-		}
1690
-
1691
-		// Of these collection types, along with our original $type, we make a
1692
-		// list of the ones for which a sharing backend has been registered.
1693
-		// FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1694
-		// with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1695
-		$allMaybeSharedItems = array();
1696
-		foreach ($collectionTypes as $collectionType) {
1697
-			if (isset(self::$backends[$collectionType])) {
1698
-				$allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1699
-					$collectionType,
1700
-					$user,
1701
-					self::FORMAT_NONE
1702
-				);
1703
-			}
1704
-		}
1705
-
1706
-		$owners = array();
1707
-		if ($includeOwner) {
1708
-			$owners[] = $user;
1709
-		}
1710
-
1711
-		// We take a look at all shared items of the given $type (or of the collections it is part of)
1712
-		// and find out their owners. Then, we gather the tags for the original $type from all owners,
1713
-		// and return them as elements of a list that look like "Tag (owner)".
1714
-		foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1715
-			foreach ($maybeSharedItems as $sharedItem) {
1716
-				if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1717
-					$owners[] = $sharedItem['uid_owner'];
1718
-				}
1719
-			}
1720
-		}
1721
-
1722
-		return $owners;
1723
-	}
1724
-
1725
-	/**
1726
-	 * Get shared items from the database
1727
-	 * @param string $itemType
1728
-	 * @param string $item Item source or target (optional)
1729
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1730
-	 * @param string $shareWith User or group the item is being shared with
1731
-	 * @param string $uidOwner User that is the owner of shared items (optional)
1732
-	 * @param int $format Format to convert items to with formatItems() (optional)
1733
-	 * @param mixed $parameters to pass to formatItems() (optional)
1734
-	 * @param int $limit Number of items to return, -1 to return all matches (optional)
1735
-	 * @param boolean $includeCollections Include collection item types (optional)
1736
-	 * @param boolean $itemShareWithBySource (optional)
1737
-	 * @param boolean $checkExpireDate
1738
-	 * @return array
1739
-	 *
1740
-	 * See public functions getItem(s)... for parameter usage
1741
-	 *
1742
-	 */
1743
-	public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1744
-									$uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1745
-									$includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1746
-		if (!self::isEnabled()) {
1747
-			return array();
1748
-		}
1749
-		$backend = self::getBackend($itemType);
1750
-		$collectionTypes = false;
1751
-		// Get filesystem root to add it to the file target and remove from the
1752
-		// file source, match file_source with the file cache
1753
-		if ($itemType == 'file' || $itemType == 'folder') {
1754
-			if(!is_null($uidOwner)) {
1755
-				$root = \OC\Files\Filesystem::getRoot();
1756
-			} else {
1757
-				$root = '';
1758
-			}
1759
-			$where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1760
-			if (!isset($item)) {
1761
-				$where .= ' AND `file_target` IS NOT NULL ';
1762
-			}
1763
-			$where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1764
-			$fileDependent = true;
1765
-			$queryArgs = array();
1766
-		} else {
1767
-			$fileDependent = false;
1768
-			$root = '';
1769
-			$collectionTypes = self::getCollectionItemTypes($itemType);
1770
-			if ($includeCollections && !isset($item) && $collectionTypes) {
1771
-				// If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1772
-				if (!in_array($itemType, $collectionTypes)) {
1773
-					$itemTypes = array_merge(array($itemType), $collectionTypes);
1774
-				} else {
1775
-					$itemTypes = $collectionTypes;
1776
-				}
1777
-				$placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1778
-				$where = ' WHERE `item_type` IN ('.$placeholders.'))';
1779
-				$queryArgs = $itemTypes;
1780
-			} else {
1781
-				$where = ' WHERE `item_type` = ?';
1782
-				$queryArgs = array($itemType);
1783
-			}
1784
-		}
1785
-		if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1786
-			$where .= ' AND `share_type` != ?';
1787
-			$queryArgs[] = self::SHARE_TYPE_LINK;
1788
-		}
1789
-		if (isset($shareType)) {
1790
-			// Include all user and group items
1791
-			if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1792
-				$where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1793
-				$queryArgs[] = self::SHARE_TYPE_USER;
1794
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
1795
-				$queryArgs[] = $shareWith;
1796
-
1797
-				$user = \OC::$server->getUserManager()->get($shareWith);
1798
-				$groups = [];
1799
-				if ($user) {
1800
-					$groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
1801
-				}
1802
-				if (!empty($groups)) {
1803
-					$placeholders = join(',', array_fill(0, count($groups), '?'));
1804
-					$where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1805
-					$queryArgs[] = self::SHARE_TYPE_GROUP;
1806
-					$queryArgs = array_merge($queryArgs, $groups);
1807
-				}
1808
-				$where .= ')';
1809
-				// Don't include own group shares
1810
-				$where .= ' AND `uid_owner` != ?';
1811
-				$queryArgs[] = $shareWith;
1812
-			} else {
1813
-				$where .= ' AND `share_type` = ?';
1814
-				$queryArgs[] = $shareType;
1815
-				if (isset($shareWith)) {
1816
-					$where .= ' AND `share_with` = ?';
1817
-					$queryArgs[] = $shareWith;
1818
-				}
1819
-			}
1820
-		}
1821
-		if (isset($uidOwner)) {
1822
-			$where .= ' AND `uid_owner` = ?';
1823
-			$queryArgs[] = $uidOwner;
1824
-			if (!isset($shareType)) {
1825
-				// Prevent unique user targets for group shares from being selected
1826
-				$where .= ' AND `share_type` != ?';
1827
-				$queryArgs[] = self::$shareTypeGroupUserUnique;
1828
-			}
1829
-			if ($fileDependent) {
1830
-				$column = 'file_source';
1831
-			} else {
1832
-				$column = 'item_source';
1833
-			}
1834
-		} else {
1835
-			if ($fileDependent) {
1836
-				$column = 'file_target';
1837
-			} else {
1838
-				$column = 'item_target';
1839
-			}
1840
-		}
1841
-		if (isset($item)) {
1842
-			$collectionTypes = self::getCollectionItemTypes($itemType);
1843
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1844
-				$where .= ' AND (';
1845
-			} else {
1846
-				$where .= ' AND';
1847
-			}
1848
-			// If looking for own shared items, check item_source else check item_target
1849
-			if (isset($uidOwner) || $itemShareWithBySource) {
1850
-				// If item type is a file, file source needs to be checked in case the item was converted
1851
-				if ($fileDependent) {
1852
-					$where .= ' `file_source` = ?';
1853
-					$column = 'file_source';
1854
-				} else {
1855
-					$where .= ' `item_source` = ?';
1856
-					$column = 'item_source';
1857
-				}
1858
-			} else {
1859
-				if ($fileDependent) {
1860
-					$where .= ' `file_target` = ?';
1861
-					$item = \OC\Files\Filesystem::normalizePath($item);
1862
-				} else {
1863
-					$where .= ' `item_target` = ?';
1864
-				}
1865
-			}
1866
-			$queryArgs[] = $item;
1867
-			if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1868
-				$placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1869
-				$where .= ' OR `item_type` IN ('.$placeholders.'))';
1870
-				$queryArgs = array_merge($queryArgs, $collectionTypes);
1871
-			}
1872
-		}
1873
-
1874
-		if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1875
-			// Make sure the unique user target is returned if it exists,
1876
-			// unique targets should follow the group share in the database
1877
-			// If the limit is not 1, the filtering can be done later
1878
-			$where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1879
-		} else {
1880
-			$where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1881
-		}
1882
-
1883
-		if ($limit != -1 && !$includeCollections) {
1884
-			// The limit must be at least 3, because filtering needs to be done
1885
-			if ($limit < 3) {
1886
-				$queryLimit = 3;
1887
-			} else {
1888
-				$queryLimit = $limit;
1889
-			}
1890
-		} else {
1891
-			$queryLimit = null;
1892
-		}
1893
-		$select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1894
-		$root = strlen($root);
1895
-		$query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1896
-		$result = $query->execute($queryArgs);
1897
-		if ($result === false) {
1898
-			\OCP\Util::writeLog('OCP\Share',
1899
-				\OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1900
-				\OCP\Util::ERROR);
1901
-		}
1902
-		$items = array();
1903
-		$targets = array();
1904
-		$switchedItems = array();
1905
-		$mounts = array();
1906
-		while ($row = $result->fetchRow()) {
1907
-			self::transformDBResults($row);
1908
-			// Filter out duplicate group shares for users with unique targets
1909
-			if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1910
-				continue;
1911
-			}
1912
-			if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1913
-				$row['share_type'] = self::SHARE_TYPE_GROUP;
1914
-				$row['unique_name'] = true; // remember that we use a unique name for this user
1915
-				$row['share_with'] = $items[$row['parent']]['share_with'];
1916
-				// if the group share was unshared from the user we keep the permission, otherwise
1917
-				// we take the permission from the parent because this is always the up-to-date
1918
-				// permission for the group share
1919
-				if ($row['permissions'] > 0) {
1920
-					$row['permissions'] = $items[$row['parent']]['permissions'];
1921
-				}
1922
-				// Remove the parent group share
1923
-				unset($items[$row['parent']]);
1924
-				if ($row['permissions'] == 0) {
1925
-					continue;
1926
-				}
1927
-			} else if (!isset($uidOwner)) {
1928
-				// Check if the same target already exists
1929
-				if (isset($targets[$row['id']])) {
1930
-					// Check if the same owner shared with the user twice
1931
-					// through a group and user share - this is allowed
1932
-					$id = $targets[$row['id']];
1933
-					if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1934
-						// Switch to group share type to ensure resharing conditions aren't bypassed
1935
-						if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1936
-							$items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1937
-							$items[$id]['share_with'] = $row['share_with'];
1938
-						}
1939
-						// Switch ids if sharing permission is granted on only
1940
-						// one share to ensure correct parent is used if resharing
1941
-						if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1942
-							&& (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1943
-							$items[$row['id']] = $items[$id];
1944
-							$switchedItems[$id] = $row['id'];
1945
-							unset($items[$id]);
1946
-							$id = $row['id'];
1947
-						}
1948
-						$items[$id]['permissions'] |= (int)$row['permissions'];
1949
-
1950
-					}
1951
-					continue;
1952
-				} elseif (!empty($row['parent'])) {
1953
-					$targets[$row['parent']] = $row['id'];
1954
-				}
1955
-			}
1956
-			// Remove root from file source paths if retrieving own shared items
1957
-			if (isset($uidOwner) && isset($row['path'])) {
1958
-				if (isset($row['parent'])) {
1959
-					$query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1960
-					$parentResult = $query->execute(array($row['parent']));
1961
-					if ($result === false) {
1962
-						\OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1963
-							\OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1964
-							\OCP\Util::ERROR);
1965
-					} else {
1966
-						$parentRow = $parentResult->fetchRow();
1967
-						$tmpPath = $parentRow['file_target'];
1968
-						// find the right position where the row path continues from the target path
1969
-						$pos = strrpos($row['path'], $parentRow['file_target']);
1970
-						$subPath = substr($row['path'], $pos);
1971
-						$splitPath = explode('/', $subPath);
1972
-						foreach (array_slice($splitPath, 2) as $pathPart) {
1973
-							$tmpPath = $tmpPath . '/' . $pathPart;
1974
-						}
1975
-						$row['path'] = $tmpPath;
1976
-					}
1977
-				} else {
1978
-					if (!isset($mounts[$row['storage']])) {
1979
-						$mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1980
-						if (is_array($mountPoints) && !empty($mountPoints)) {
1981
-							$mounts[$row['storage']] = current($mountPoints);
1982
-						}
1983
-					}
1984
-					if (!empty($mounts[$row['storage']])) {
1985
-						$path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1986
-						$relPath = substr($path, $root); // path relative to data/user
1987
-						$row['path'] = rtrim($relPath, '/');
1988
-					}
1989
-				}
1990
-			}
1991
-
1992
-			if($checkExpireDate) {
1993
-				if (self::expireItem($row)) {
1994
-					continue;
1995
-				}
1996
-			}
1997
-			// Check if resharing is allowed, if not remove share permission
1998
-			if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
1999
-				$row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
2000
-			}
2001
-			// Add display names to result
2002
-			$row['share_with_displayname'] = $row['share_with'];
2003
-			if ( isset($row['share_with']) && $row['share_with'] != '' &&
2004
-				$row['share_type'] === self::SHARE_TYPE_USER) {
2005
-				$row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
2006
-			} else if(isset($row['share_with']) && $row['share_with'] != '' &&
2007
-				$row['share_type'] === self::SHARE_TYPE_REMOTE) {
2008
-				$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
2009
-				foreach ($addressBookEntries as $entry) {
2010
-					foreach ($entry['CLOUD'] as $cloudID) {
2011
-						if ($cloudID === $row['share_with']) {
2012
-							$row['share_with_displayname'] = $entry['FN'];
2013
-						}
2014
-					}
2015
-				}
2016
-			}
2017
-			if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
2018
-				$row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
2019
-			}
2020
-
2021
-			if ($row['permissions'] > 0) {
2022
-				$items[$row['id']] = $row;
2023
-			}
2024
-
2025
-		}
2026
-
2027
-		// group items if we are looking for items shared with the current user
2028
-		if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
2029
-			$items = self::groupItems($items, $itemType);
2030
-		}
2031
-
2032
-		if (!empty($items)) {
2033
-			$collectionItems = array();
2034
-			foreach ($items as &$row) {
2035
-				// Return only the item instead of a 2-dimensional array
2036
-				if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
2037
-					if ($format == self::FORMAT_NONE) {
2038
-						return $row;
2039
-					} else {
2040
-						break;
2041
-					}
2042
-				}
2043
-				// Check if this is a collection of the requested item type
2044
-				if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
2045
-					if (($collectionBackend = self::getBackend($row['item_type']))
2046
-						&& $collectionBackend instanceof \OCP\Share_Backend_Collection) {
2047
-						// Collections can be inside collections, check if the item is a collection
2048
-						if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
2049
-							$collectionItems[] = $row;
2050
-						} else {
2051
-							$collection = array();
2052
-							$collection['item_type'] = $row['item_type'];
2053
-							if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2054
-								$collection['path'] = basename($row['path']);
2055
-							}
2056
-							$row['collection'] = $collection;
2057
-							// Fetch all of the children sources
2058
-							$children = $collectionBackend->getChildren($row[$column]);
2059
-							foreach ($children as $child) {
2060
-								$childItem = $row;
2061
-								$childItem['item_type'] = $itemType;
2062
-								if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
2063
-									$childItem['item_source'] = $child['source'];
2064
-									$childItem['item_target'] = $child['target'];
2065
-								}
2066
-								if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2067
-									if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2068
-										$childItem['file_source'] = $child['source'];
2069
-									} else { // TODO is this really needed if we already know that we use the file backend?
2070
-										$meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
2071
-										$childItem['file_source'] = $meta['fileid'];
2072
-									}
2073
-									$childItem['file_target'] =
2074
-										\OC\Files\Filesystem::normalizePath($child['file_path']);
2075
-								}
2076
-								if (isset($item)) {
2077
-									if ($childItem[$column] == $item) {
2078
-										// Return only the item instead of a 2-dimensional array
2079
-										if ($limit == 1) {
2080
-											if ($format == self::FORMAT_NONE) {
2081
-												return $childItem;
2082
-											} else {
2083
-												// Unset the items array and break out of both loops
2084
-												$items = array();
2085
-												$items[] = $childItem;
2086
-												break 2;
2087
-											}
2088
-										} else {
2089
-											$collectionItems[] = $childItem;
2090
-										}
2091
-									}
2092
-								} else {
2093
-									$collectionItems[] = $childItem;
2094
-								}
2095
-							}
2096
-						}
2097
-					}
2098
-					// Remove collection item
2099
-					$toRemove = $row['id'];
2100
-					if (array_key_exists($toRemove, $switchedItems)) {
2101
-						$toRemove = $switchedItems[$toRemove];
2102
-					}
2103
-					unset($items[$toRemove]);
2104
-				} elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
2105
-					// FIXME: Thats a dirty hack to improve file sharing performance,
2106
-					// see github issue #10588 for more details
2107
-					// Need to find a solution which works for all back-ends
2108
-					$collectionBackend = self::getBackend($row['item_type']);
2109
-					$sharedParents = $collectionBackend->getParents($row['item_source']);
2110
-					foreach ($sharedParents as $parent) {
2111
-						$collectionItems[] = $parent;
2112
-					}
2113
-				}
2114
-			}
2115
-			if (!empty($collectionItems)) {
2116
-				$collectionItems = array_unique($collectionItems, SORT_REGULAR);
2117
-				$items = array_merge($items, $collectionItems);
2118
-			}
2119
-
2120
-			// filter out invalid items, these can appear when subshare entries exist
2121
-			// for a group in which the requested user isn't a member any more
2122
-			$items = array_filter($items, function($item) {
2123
-				return $item['share_type'] !== self::$shareTypeGroupUserUnique;
2124
-			});
2125
-
2126
-			return self::formatResult($items, $column, $backend, $format, $parameters);
2127
-		} elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
2128
-			// FIXME: Thats a dirty hack to improve file sharing performance,
2129
-			// see github issue #10588 for more details
2130
-			// Need to find a solution which works for all back-ends
2131
-			$collectionItems = array();
2132
-			$collectionBackend = self::getBackend('folder');
2133
-			$sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
2134
-			foreach ($sharedParents as $parent) {
2135
-				$collectionItems[] = $parent;
2136
-			}
2137
-			if ($limit === 1) {
2138
-				return reset($collectionItems);
2139
-			}
2140
-			return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
2141
-		}
2142
-
2143
-		return array();
2144
-	}
2145
-
2146
-	/**
2147
-	 * group items with link to the same source
2148
-	 *
2149
-	 * @param array $items
2150
-	 * @param string $itemType
2151
-	 * @return array of grouped items
2152
-	 */
2153
-	protected static function groupItems($items, $itemType) {
2154
-
2155
-		$fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
2156
-
2157
-		$result = array();
2158
-
2159
-		foreach ($items as $item) {
2160
-			$grouped = false;
2161
-			foreach ($result as $key => $r) {
2162
-				// for file/folder shares we need to compare file_source, otherwise we compare item_source
2163
-				// only group shares if they already point to the same target, otherwise the file where shared
2164
-				// before grouping of shares was added. In this case we don't group them toi avoid confusions
2165
-				if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2166
-					(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2167
-					// add the first item to the list of grouped shares
2168
-					if (!isset($result[$key]['grouped'])) {
2169
-						$result[$key]['grouped'][] = $result[$key];
2170
-					}
2171
-					$result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
2172
-					$result[$key]['grouped'][] = $item;
2173
-					$grouped = true;
2174
-					break;
2175
-				}
2176
-			}
2177
-
2178
-			if (!$grouped) {
2179
-				$result[] = $item;
2180
-			}
2181
-
2182
-		}
2183
-
2184
-		return $result;
2185
-	}
2186
-
2187
-	/**
2188
-	 * Put shared item into the database
2189
-	 * @param string $itemType Item type
2190
-	 * @param string $itemSource Item source
2191
-	 * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
2192
-	 * @param string $shareWith User or group the item is being shared with
2193
-	 * @param string $uidOwner User that is the owner of shared item
2194
-	 * @param int $permissions CRUDS permissions
2195
-	 * @param boolean|array $parentFolder Parent folder target (optional)
2196
-	 * @param string $token (optional)
2197
-	 * @param string $itemSourceName name of the source item (optional)
2198
-	 * @param \DateTime $expirationDate (optional)
2199
-	 * @throws \Exception
2200
-	 * @return mixed id of the new share or false
2201
-	 */
2202
-	private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2203
-								$permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
2204
-
2205
-		$queriesToExecute = array();
2206
-		$suggestedItemTarget = null;
2207
-		$groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
2208
-		$groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2209
-
2210
-		$result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2211
-		if(!empty($result)) {
2212
-			$parent = $result['parent'];
2213
-			$itemSource = $result['itemSource'];
2214
-			$fileSource = $result['fileSource'];
2215
-			$suggestedItemTarget = $result['suggestedItemTarget'];
2216
-			$suggestedFileTarget = $result['suggestedFileTarget'];
2217
-			$filePath = $result['filePath'];
2218
-		}
2219
-
2220
-		$isGroupShare = false;
2221
-		if ($shareType == self::SHARE_TYPE_GROUP) {
2222
-			$isGroupShare = true;
2223
-			if (isset($shareWith['users'])) {
2224
-				$users = $shareWith['users'];
2225
-			} else {
2226
-				$group = \OC::$server->getGroupManager()->get($shareWith['group']);
2227
-				if ($group) {
2228
-					$users = $group->searchUsers('', -1, 0);
2229
-					$userIds = [];
2230
-					foreach ($users as $user) {
2231
-						$userIds[] = $user->getUID();
2232
-					}
2233
-					$users = $userIds;
2234
-				} else {
2235
-					$users = [];
2236
-				}
2237
-			}
2238
-			// remove current user from list
2239
-			if (in_array(\OCP\User::getUser(), $users)) {
2240
-				unset($users[array_search(\OCP\User::getUser(), $users)]);
2241
-			}
2242
-			$groupItemTarget = Helper::generateTarget($itemType, $itemSource,
2243
-				$shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
2244
-			$groupFileTarget = Helper::generateTarget($itemType, $itemSource,
2245
-				$shareType, $shareWith['group'], $uidOwner, $filePath);
2246
-
2247
-			// add group share to table and remember the id as parent
2248
-			$queriesToExecute['groupShare'] = array(
2249
-				'itemType'			=> $itemType,
2250
-				'itemSource'		=> $itemSource,
2251
-				'itemTarget'		=> $groupItemTarget,
2252
-				'shareType'			=> $shareType,
2253
-				'shareWith'			=> $shareWith['group'],
2254
-				'uidOwner'			=> $uidOwner,
2255
-				'permissions'		=> $permissions,
2256
-				'shareTime'			=> time(),
2257
-				'fileSource'		=> $fileSource,
2258
-				'fileTarget'		=> $groupFileTarget,
2259
-				'token'				=> $token,
2260
-				'parent'			=> $parent,
2261
-				'expiration'		=> $expirationDate,
2262
-			);
2263
-
2264
-		} else {
2265
-			$users = array($shareWith);
2266
-			$itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2267
-				$suggestedItemTarget);
2268
-		}
2269
-
2270
-		$run = true;
2271
-		$error = '';
2272
-		$preHookData = array(
2273
-			'itemType' => $itemType,
2274
-			'itemSource' => $itemSource,
2275
-			'shareType' => $shareType,
2276
-			'uidOwner' => $uidOwner,
2277
-			'permissions' => $permissions,
2278
-			'fileSource' => $fileSource,
2279
-			'expiration' => $expirationDate,
2280
-			'token' => $token,
2281
-			'run' => &$run,
2282
-			'error' => &$error
2283
-		);
2284
-
2285
-		$preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2286
-		$preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2287
-
2288
-		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
2289
-
2290
-		if ($run === false) {
2291
-			throw new \Exception($error);
2292
-		}
2293
-
2294
-		foreach ($users as $user) {
2295
-			$sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
2296
-			$sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
2297
-
2298
-			$userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
2299
-
2300
-			if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
2301
-				$fileTarget = $sourceExists['file_target'];
2302
-				$itemTarget = $sourceExists['item_target'];
2303
-
2304
-				// for group shares we don't need a additional entry if the target is the same
2305
-				if($isGroupShare && $groupItemTarget === $itemTarget) {
2306
-					continue;
2307
-				}
2308
-
2309
-			} elseif(!$sourceExists && !$isGroupShare)  {
2310
-
2311
-				$itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2312
-					$uidOwner, $suggestedItemTarget, $parent);
2313
-				if (isset($fileSource)) {
2314
-					if ($parentFolder) {
2315
-						if ($parentFolder === true) {
2316
-							$fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
2317
-								$uidOwner, $suggestedFileTarget, $parent);
2318
-							if ($fileTarget != $groupFileTarget) {
2319
-								$parentFolders[$user]['folder'] = $fileTarget;
2320
-							}
2321
-						} else if (isset($parentFolder[$user])) {
2322
-							$fileTarget = $parentFolder[$user]['folder'].$itemSource;
2323
-							$parent = $parentFolder[$user]['id'];
2324
-						}
2325
-					} else {
2326
-						$fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
2327
-							$user, $uidOwner, $suggestedFileTarget, $parent);
2328
-					}
2329
-				} else {
2330
-					$fileTarget = null;
2331
-				}
2332
-
2333
-			} else {
2334
-
2335
-				// group share which doesn't exists until now, check if we need a unique target for this user
2336
-
2337
-				$itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2338
-					$uidOwner, $suggestedItemTarget, $parent);
2339
-
2340
-				// do we also need a file target
2341
-				if (isset($fileSource)) {
2342
-					$fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2343
-						$uidOwner, $suggestedFileTarget, $parent);
2344
-				} else {
2345
-					$fileTarget = null;
2346
-				}
2347
-
2348
-				if (($itemTarget === $groupItemTarget) &&
2349
-					(!isset($fileSource) || $fileTarget === $groupFileTarget)) {
2350
-					continue;
2351
-				}
2352
-			}
2353
-
2354
-			$queriesToExecute[] = array(
2355
-				'itemType'			=> $itemType,
2356
-				'itemSource'		=> $itemSource,
2357
-				'itemTarget'		=> $itemTarget,
2358
-				'shareType'			=> $userShareType,
2359
-				'shareWith'			=> $user,
2360
-				'uidOwner'			=> $uidOwner,
2361
-				'permissions'		=> $permissions,
2362
-				'shareTime'			=> time(),
2363
-				'fileSource'		=> $fileSource,
2364
-				'fileTarget'		=> $fileTarget,
2365
-				'token'				=> $token,
2366
-				'parent'			=> $parent,
2367
-				'expiration'		=> $expirationDate,
2368
-			);
2369
-
2370
-		}
2371
-
2372
-		$id = false;
2373
-		if ($isGroupShare) {
2374
-			$id = self::insertShare($queriesToExecute['groupShare']);
2375
-			// Save this id, any extra rows for this group share will need to reference it
2376
-			$parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2377
-			unset($queriesToExecute['groupShare']);
2378
-		}
2379
-
2380
-		foreach ($queriesToExecute as $shareQuery) {
2381
-			$shareQuery['parent'] = $parent;
2382
-			$id = self::insertShare($shareQuery);
2383
-		}
2384
-
2385
-		$postHookData = array(
2386
-			'itemType' => $itemType,
2387
-			'itemSource' => $itemSource,
2388
-			'parent' => $parent,
2389
-			'shareType' => $shareType,
2390
-			'uidOwner' => $uidOwner,
2391
-			'permissions' => $permissions,
2392
-			'fileSource' => $fileSource,
2393
-			'id' => $parent,
2394
-			'token' => $token,
2395
-			'expirationDate' => $expirationDate,
2396
-		);
2397
-
2398
-		$postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2399
-		$postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2400
-		$postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2401
-
2402
-		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2403
-
2404
-
2405
-		return $id ? $id : false;
2406
-	}
2407
-
2408
-	/**
2409
-	 * @param string $itemType
2410
-	 * @param string $itemSource
2411
-	 * @param int $shareType
2412
-	 * @param string $shareWith
2413
-	 * @param string $uidOwner
2414
-	 * @param int $permissions
2415
-	 * @param string|null $itemSourceName
2416
-	 * @param null|\DateTime $expirationDate
2417
-	 */
2418
-	private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2419
-		$backend = self::getBackend($itemType);
2420
-
2421
-		$l = \OC::$server->getL10N('lib');
2422
-		$result = array();
2423
-
2424
-		$column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2425
-
2426
-		$checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2427
-		if ($checkReshare) {
2428
-			// Check if attempting to share back to owner
2429
-			if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2430
-				$message = 'Sharing %s failed, because the user %s is the original sharer';
2431
-				$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
2432
-
2433
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
2434
-				throw new \Exception($message_t);
2435
-			}
2436
-		}
2437
-
2438
-		if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
2439
-			// Check if share permissions is granted
2440
-			if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2441
-				if (~(int)$checkReshare['permissions'] & $permissions) {
2442
-					$message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2443
-					$message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2444
-
2445
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
2446
-					throw new \Exception($message_t);
2447
-				} else {
2448
-					// TODO Don't check if inside folder
2449
-					$result['parent'] = $checkReshare['id'];
2450
-
2451
-					$result['expirationDate'] = $expirationDate;
2452
-					// $checkReshare['expiration'] could be null and then is always less than any value
2453
-					if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2454
-						$result['expirationDate'] = $checkReshare['expiration'];
2455
-					}
2456
-
2457
-					// only suggest the same name as new target if it is a reshare of the
2458
-					// same file/folder and not the reshare of a child
2459
-					if ($checkReshare[$column] === $itemSource) {
2460
-						$result['filePath'] = $checkReshare['file_target'];
2461
-						$result['itemSource'] = $checkReshare['item_source'];
2462
-						$result['fileSource'] = $checkReshare['file_source'];
2463
-						$result['suggestedItemTarget'] = $checkReshare['item_target'];
2464
-						$result['suggestedFileTarget'] = $checkReshare['file_target'];
2465
-					} else {
2466
-						$result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2467
-						$result['suggestedItemTarget'] = null;
2468
-						$result['suggestedFileTarget'] = null;
2469
-						$result['itemSource'] = $itemSource;
2470
-						$result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2471
-					}
2472
-				}
2473
-			} else {
2474
-				$message = 'Sharing %s failed, because resharing is not allowed';
2475
-				$message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
2476
-
2477
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
2478
-				throw new \Exception($message_t);
2479
-			}
2480
-		} else {
2481
-			$result['parent'] = null;
2482
-			$result['suggestedItemTarget'] = null;
2483
-			$result['suggestedFileTarget'] = null;
2484
-			$result['itemSource'] = $itemSource;
2485
-			$result['expirationDate'] = $expirationDate;
2486
-			if (!$backend->isValidSource($itemSource, $uidOwner)) {
2487
-				$message = 'Sharing %s failed, because the sharing backend for '
2488
-					.'%s could not find its source';
2489
-				$message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
2490
-				\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
2491
-				throw new \Exception($message_t);
2492
-			}
2493
-			if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2494
-				$result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2495
-				if ($itemType == 'file' || $itemType == 'folder') {
2496
-					$result['fileSource'] = $itemSource;
2497
-				} else {
2498
-					$meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
2499
-					$result['fileSource'] = $meta['fileid'];
2500
-				}
2501
-				if ($result['fileSource'] == -1) {
2502
-					$message = 'Sharing %s failed, because the file could not be found in the file cache';
2503
-					$message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
2504
-
2505
-					\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
2506
-					throw new \Exception($message_t);
2507
-				}
2508
-			} else {
2509
-				$result['filePath'] = null;
2510
-				$result['fileSource'] = null;
2511
-			}
2512
-		}
2513
-
2514
-		return $result;
2515
-	}
2516
-
2517
-	/**
2518
-	 *
2519
-	 * @param array $shareData
2520
-	 * @return mixed false in case of a failure or the id of the new share
2521
-	 */
2522
-	private static function insertShare(array $shareData) {
2523
-
2524
-		$query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2525
-			.' `item_type`, `item_source`, `item_target`, `share_type`,'
2526
-			.' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2527
-			.' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2528
-		$query->bindValue(1, $shareData['itemType']);
2529
-		$query->bindValue(2, $shareData['itemSource']);
2530
-		$query->bindValue(3, $shareData['itemTarget']);
2531
-		$query->bindValue(4, $shareData['shareType']);
2532
-		$query->bindValue(5, $shareData['shareWith']);
2533
-		$query->bindValue(6, $shareData['uidOwner']);
2534
-		$query->bindValue(7, $shareData['permissions']);
2535
-		$query->bindValue(8, $shareData['shareTime']);
2536
-		$query->bindValue(9, $shareData['fileSource']);
2537
-		$query->bindValue(10, $shareData['fileTarget']);
2538
-		$query->bindValue(11, $shareData['token']);
2539
-		$query->bindValue(12, $shareData['parent']);
2540
-		$query->bindValue(13, $shareData['expiration'], 'datetime');
2541
-		$result = $query->execute();
2542
-
2543
-		$id = false;
2544
-		if ($result) {
2545
-			$id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2546
-		}
2547
-
2548
-		return $id;
2549
-
2550
-	}
2551
-
2552
-	/**
2553
-	 * Delete all shares with type SHARE_TYPE_LINK
2554
-	 */
2555
-	public static function removeAllLinkShares() {
2556
-		// Delete any link shares
2557
-		$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2558
-		$result = $query->execute(array(self::SHARE_TYPE_LINK));
2559
-		while ($item = $result->fetchRow()) {
2560
-			Helper::delete($item['id']);
2561
-		}
2562
-	}
2563
-
2564
-	/**
2565
-	 * In case a password protected link is not yet authenticated this function will return false
2566
-	 *
2567
-	 * @param array $linkItem
2568
-	 * @return boolean
2569
-	 */
2570
-	public static function checkPasswordProtectedShare(array $linkItem) {
2571
-		if (!isset($linkItem['share_with'])) {
2572
-			return true;
2573
-		}
2574
-		if (!isset($linkItem['share_type'])) {
2575
-			return true;
2576
-		}
2577
-		if (!isset($linkItem['id'])) {
2578
-			return true;
2579
-		}
2580
-
2581
-		if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2582
-			return true;
2583
-		}
2584
-
2585
-		if ( \OC::$server->getSession()->exists('public_link_authenticated')
2586
-			&& \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2587
-			return true;
2588
-		}
2589
-
2590
-		return false;
2591
-	}
2592
-
2593
-	/**
2594
-	 * construct select statement
2595
-	 * @param int $format
2596
-	 * @param boolean $fileDependent ist it a file/folder share or a generla share
2597
-	 * @param string $uidOwner
2598
-	 * @return string select statement
2599
-	 */
2600
-	private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2601
-		$select = '*';
2602
-		if ($format == self::FORMAT_STATUSES) {
2603
-			if ($fileDependent) {
2604
-				$select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2605
-					. '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2606
-					. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2607
-					. '`uid_initiator`';
2608
-			} else {
2609
-				$select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2610
-			}
2611
-		} else {
2612
-			if (isset($uidOwner)) {
2613
-				if ($fileDependent) {
2614
-					$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2615
-						. ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2616
-						. ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2617
-						. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2618
-				} else {
2619
-					$select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2620
-						. ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2621
-				}
2622
-			} else {
2623
-				if ($fileDependent) {
2624
-					if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
2625
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2626
-							. '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2627
-							. '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2628
-							. '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
2629
-					} else {
2630
-						$select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2631
-							. '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2632
-							. '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2633
-						    . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2634
-							. '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2635
-					}
2636
-				}
2637
-			}
2638
-		}
2639
-		return $select;
2640
-	}
2641
-
2642
-
2643
-	/**
2644
-	 * transform db results
2645
-	 * @param array $row result
2646
-	 */
2647
-	private static function transformDBResults(&$row) {
2648
-		if (isset($row['id'])) {
2649
-			$row['id'] = (int) $row['id'];
2650
-		}
2651
-		if (isset($row['share_type'])) {
2652
-			$row['share_type'] = (int) $row['share_type'];
2653
-		}
2654
-		if (isset($row['parent'])) {
2655
-			$row['parent'] = (int) $row['parent'];
2656
-		}
2657
-		if (isset($row['file_parent'])) {
2658
-			$row['file_parent'] = (int) $row['file_parent'];
2659
-		}
2660
-		if (isset($row['file_source'])) {
2661
-			$row['file_source'] = (int) $row['file_source'];
2662
-		}
2663
-		if (isset($row['permissions'])) {
2664
-			$row['permissions'] = (int) $row['permissions'];
2665
-		}
2666
-		if (isset($row['storage'])) {
2667
-			$row['storage'] = (int) $row['storage'];
2668
-		}
2669
-		if (isset($row['stime'])) {
2670
-			$row['stime'] = (int) $row['stime'];
2671
-		}
2672
-		if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
2673
-			// discard expiration date for non-link shares, which might have been
2674
-			// set by ancient bugs
2675
-			$row['expiration'] = null;
2676
-		}
2677
-	}
2678
-
2679
-	/**
2680
-	 * format result
2681
-	 * @param array $items result
2682
-	 * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2683
-	 * @param \OCP\Share_Backend $backend sharing backend
2684
-	 * @param int $format
2685
-	 * @param array $parameters additional format parameters
2686
-	 * @return array format result
2687
-	 */
2688
-	private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2689
-		if ($format === self::FORMAT_NONE) {
2690
-			return $items;
2691
-		} else if ($format === self::FORMAT_STATUSES) {
2692
-			$statuses = array();
2693
-			foreach ($items as $item) {
2694
-				if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2695
-					if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
2696
-						continue;
2697
-					}
2698
-					$statuses[$item[$column]]['link'] = true;
2699
-				} else if (!isset($statuses[$item[$column]])) {
2700
-					$statuses[$item[$column]]['link'] = false;
2701
-				}
2702
-				if (!empty($item['file_target'])) {
2703
-					$statuses[$item[$column]]['path'] = $item['path'];
2704
-				}
2705
-			}
2706
-			return $statuses;
2707
-		} else {
2708
-			return $backend->formatItems($items, $format, $parameters);
2709
-		}
2710
-	}
2711
-
2712
-	/**
2713
-	 * remove protocol from URL
2714
-	 *
2715
-	 * @param string $url
2716
-	 * @return string
2717
-	 */
2718
-	public static function removeProtocolFromUrl($url) {
2719
-		if (strpos($url, 'https://') === 0) {
2720
-			return substr($url, strlen('https://'));
2721
-		} else if (strpos($url, 'http://') === 0) {
2722
-			return substr($url, strlen('http://'));
2723
-		}
2724
-
2725
-		return $url;
2726
-	}
2727
-
2728
-	/**
2729
-	 * try http post first with https and then with http as a fallback
2730
-	 *
2731
-	 * @param string $remoteDomain
2732
-	 * @param string $urlSuffix
2733
-	 * @param array $fields post parameters
2734
-	 * @return array
2735
-	 */
2736
-	private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
2737
-		$protocol = 'https://';
2738
-		$result = [
2739
-			'success' => false,
2740
-			'result' => '',
2741
-		];
2742
-		$try = 0;
2743
-		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
2744
-		while ($result['success'] === false && $try < 2) {
2745
-			$federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
2746
-			$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
2747
-			$result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields);
2748
-			$try++;
2749
-			$protocol = 'http://';
2750
-		}
2751
-
2752
-		return $result;
2753
-	}
2754
-
2755
-	/**
2756
-	 * send server-to-server share to remote server
2757
-	 *
2758
-	 * @param string $token
2759
-	 * @param string $shareWith
2760
-	 * @param string $name
2761
-	 * @param int $remote_id
2762
-	 * @param string $owner
2763
-	 * @return bool
2764
-	 */
2765
-	private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2766
-
2767
-		list($user, $remote) = Helper::splitUserRemote($shareWith);
2768
-
2769
-		if ($user && $remote) {
2770
-			$url = $remote;
2771
-
2772
-			$local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2773
-
2774
-			$fields = array(
2775
-				'shareWith' => $user,
2776
-				'token' => $token,
2777
-				'name' => $name,
2778
-				'remoteId' => $remote_id,
2779
-				'owner' => $owner,
2780
-				'remote' => $local,
2781
-			);
2782
-
2783
-			$url = self::removeProtocolFromUrl($url);
2784
-			$result = self::tryHttpPostToShareEndpoint($url, '', $fields);
2785
-			$status = json_decode($result['result'], true);
2786
-
2787
-			if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
2788
-				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
2789
-				return true;
2790
-			}
2791
-
2792
-		}
2793
-
2794
-		return false;
2795
-	}
2796
-
2797
-	/**
2798
-	 * send server-to-server unshare to remote server
2799
-	 *
2800
-	 * @param string $remote url
2801
-	 * @param int $id share id
2802
-	 * @param string $token
2803
-	 * @return bool
2804
-	 */
2805
-	private static function sendRemoteUnshare($remote, $id, $token) {
2806
-		$url = rtrim($remote, '/');
2807
-		$fields = array('token' => $token, 'format' => 'json');
2808
-		$url = self::removeProtocolFromUrl($url);
2809
-		$result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
2810
-		$status = json_decode($result['result'], true);
2811
-
2812
-		return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
2813
-	}
2814
-
2815
-	/**
2816
-	 * check if user can only share with group members
2817
-	 * @return bool
2818
-	 */
2819
-	public static function shareWithGroupMembersOnly() {
2820
-		$value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no');
2821
-		return ($value === 'yes') ? true : false;
2822
-	}
2823
-
2824
-	/**
2825
-	 * @return bool
2826
-	 */
2827
-	public static function isDefaultExpireDateEnabled() {
2828
-		$defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2829
-		return ($defaultExpireDateEnabled === "yes") ? true : false;
2830
-	}
2831
-
2832
-	/**
2833
-	 * @return bool
2834
-	 */
2835
-	public static function enforceDefaultExpireDate() {
2836
-		$enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2837
-		return ($enforceDefaultExpireDate === "yes") ? true : false;
2838
-	}
2839
-
2840
-	/**
2841
-	 * @return int
2842
-	 */
2843
-	public static function getExpireInterval() {
2844
-		return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2845
-	}
2846
-
2847
-	/**
2848
-	 * Checks whether the given path is reachable for the given owner
2849
-	 *
2850
-	 * @param string $path path relative to files
2851
-	 * @param string $ownerStorageId storage id of the owner
2852
-	 *
2853
-	 * @return boolean true if file is reachable, false otherwise
2854
-	 */
2855
-	private static function isFileReachable($path, $ownerStorageId) {
2856
-		// if outside the home storage, file is always considered reachable
2857
-		if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
2858
-			substr($ownerStorageId, 0, 13) === 'object::user:'
2859
-		)) {
2860
-			return true;
2861
-		}
2862
-
2863
-		// if inside the home storage, the file has to be under "/files/"
2864
-		$path = ltrim($path, '/');
2865
-		if (substr($path, 0, 6) === 'files/') {
2866
-			return true;
2867
-		}
2868
-
2869
-		return false;
2870
-	}
2871
-
2872
-	/**
2873
-	 * @param IConfig $config
2874
-	 * @return bool
2875
-	 */
2876
-	public static function enforcePassword(IConfig $config) {
2877
-		$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2878
-		return ($enforcePassword === "yes") ? true : false;
2879
-	}
2880
-
2881
-	/**
2882
-	 * Get all share entries, including non-unique group items
2883
-	 *
2884
-	 * @param string $owner
2885
-	 * @return array
2886
-	 */
2887
-	public static function getAllSharesForOwner($owner) {
2888
-		$query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?';
2889
-		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]);
2890
-		return $result->fetchAll();
2891
-	}
2892
-
2893
-	/**
2894
-	 * Get all share entries, including non-unique group items for a file
2895
-	 *
2896
-	 * @param int $id
2897
-	 * @return array
2898
-	 */
2899
-	public static function getAllSharesForFileId($id) {
2900
-		$query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?';
2901
-		$result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
2902
-		return $result->fetchAll();
2903
-	}
2904
-
2905
-	/**
2906
-	 * @param string $password
2907
-	 * @throws \Exception
2908
-	 */
2909
-	private static function verifyPassword($password) {
2910
-
2911
-		$accepted = true;
2912
-		$message = '';
2913
-		\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
2914
-			'password' => $password,
2915
-			'accepted' => &$accepted,
2916
-			'message' => &$message
2917
-		]);
2918
-
2919
-		if (!$accepted) {
2920
-			throw new \Exception($message);
2921
-		}
2922
-	}
1310
+            if ($permissions & ~(int)$rootItem['permissions']) {
1311
+                $qb = $connection->getQueryBuilder();
1312
+                $qb->select('id', 'permissions', 'item_type')
1313
+                    ->from('share')
1314
+                    ->where($qb->expr()->eq('parent', $qb->createParameter('parent')))
1315
+                    ->andWhere($qb->expr()->eq('share_type', $qb->createParameter('share_type')))
1316
+                    ->andWhere($qb->expr()->neq('permissions', $qb->createParameter('shareDeleted')))
1317
+                    ->setParameter(':parent', (int)$rootItem['id'])
1318
+                    ->setParameter(':share_type', 2)
1319
+                    ->setParameter(':shareDeleted', 0);
1320
+                $result = $qb->execute();
1321
+
1322
+                $ids = [];
1323
+                while ($item = $result->fetch()) {
1324
+                    $item = $sanitizeItem($item);
1325
+                    $items[] = $item;
1326
+                    $ids[] = $item['id'];
1327
+                }
1328
+                $result->closeCursor();
1329
+
1330
+                // Add permssions for all USERGROUP shares of this item
1331
+                if (!empty($ids)) {
1332
+                    $ids = $intArrayToLiteralArray($ids, $qb->expr());
1333
+
1334
+                    $qb = $connection->getQueryBuilder();
1335
+                    $qb->update('share')
1336
+                        ->set('permissions', $qb->createParameter('permissions'))
1337
+                        ->where($qb->expr()->in('id', $ids))
1338
+                        ->setParameter(':permissions', $permissions);
1339
+                    $qb->execute();
1340
+                }
1341
+            }
1342
+
1343
+            foreach ($items as $item) {
1344
+                \OC_Hook::emit('OCP\Share', 'post_update_permissions', ['share' => $item]);
1345
+            }
1346
+
1347
+            return true;
1348
+        }
1349
+        $message = 'Setting permissions for %s failed, because the item was not found';
1350
+        $message_t = $l->t('Setting permissions for %s failed, because the item was not found', array($itemSource));
1351
+
1352
+        \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
1353
+        throw new \Exception($message_t);
1354
+    }
1355
+
1356
+    /**
1357
+     * validate expiration date if it meets all constraints
1358
+     *
1359
+     * @param string $expireDate well formatted date string, e.g. "DD-MM-YYYY"
1360
+     * @param string $shareTime timestamp when the file was shared
1361
+     * @param string $itemType
1362
+     * @param string $itemSource
1363
+     * @return \DateTime validated date
1364
+     * @throws \Exception when the expire date is in the past or further in the future then the enforced date
1365
+     */
1366
+    private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) {
1367
+        $l = \OC::$server->getL10N('lib');
1368
+        $date = new \DateTime($expireDate);
1369
+        $today = new \DateTime('now');
1370
+
1371
+        // if the user doesn't provide a share time we need to get it from the database
1372
+        // fall-back mode to keep API stable, because the $shareTime parameter was added later
1373
+        $defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced();
1374
+        if ($defaultExpireDateEnforced && $shareTime === null) {
1375
+            $items = self::getItemShared($itemType, $itemSource);
1376
+            $firstItem = reset($items);
1377
+            $shareTime = (int)$firstItem['stime'];
1378
+        }
1379
+
1380
+        if ($defaultExpireDateEnforced) {
1381
+            // initialize max date with share time
1382
+            $maxDate = new \DateTime();
1383
+            $maxDate->setTimestamp($shareTime);
1384
+            $maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
1385
+            $maxDate->add(new \DateInterval('P' . $maxDays . 'D'));
1386
+            if ($date > $maxDate) {
1387
+                $warning = 'Cannot set expiration date. Shares cannot expire later than ' . $maxDays . ' after they have been shared';
1388
+                $warning_t = $l->t('Cannot set expiration date. Shares cannot expire later than %s after they have been shared', array($maxDays));
1389
+                \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1390
+                throw new \Exception($warning_t);
1391
+            }
1392
+        }
1393
+
1394
+        if ($date < $today) {
1395
+            $message = 'Cannot set expiration date. Expiration date is in the past';
1396
+            $message_t = $l->t('Cannot set expiration date. Expiration date is in the past');
1397
+            \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN);
1398
+            throw new \Exception($message_t);
1399
+        }
1400
+
1401
+        return $date;
1402
+    }
1403
+
1404
+    /**
1405
+     * Set expiration date for a share
1406
+     * @param string $itemType
1407
+     * @param string $itemSource
1408
+     * @param string $date expiration date
1409
+     * @param int $shareTime timestamp from when the file was shared
1410
+     * @return boolean
1411
+     * @throws \Exception when the expire date is not set, in the past or further in the future then the enforced date
1412
+     */
1413
+    public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) {
1414
+        $user = \OC_User::getUser();
1415
+        $l = \OC::$server->getL10N('lib');
1416
+
1417
+        if ($date == '') {
1418
+            if (\OCP\Util::isDefaultExpireDateEnforced()) {
1419
+                $warning = 'Cannot clear expiration date. Shares are required to have an expiration date.';
1420
+                $warning_t = $l->t('Cannot clear expiration date. Shares are required to have an expiration date.');
1421
+                \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN);
1422
+                throw new \Exception($warning_t);
1423
+            } else {
1424
+                $date = null;
1425
+            }
1426
+        } else {
1427
+            $date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource);
1428
+        }
1429
+        $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ?  AND `uid_owner` = ? AND `share_type` = ?');
1430
+        $query->bindValue(1, $date, 'datetime');
1431
+        $query->bindValue(2, $itemType);
1432
+        $query->bindValue(3, $itemSource);
1433
+        $query->bindValue(4, $user);
1434
+        $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK);
1435
+
1436
+        $query->execute();
1437
+
1438
+        \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', array(
1439
+            'itemType' => $itemType,
1440
+            'itemSource' => $itemSource,
1441
+            'date' => $date,
1442
+            'uidOwner' => $user
1443
+        ));
1444
+
1445
+        return true;
1446
+    }
1447
+
1448
+    /**
1449
+     * Retrieve the owner of a connection
1450
+     *
1451
+     * @param IDBConnection $connection
1452
+     * @param int $shareId
1453
+     * @throws \Exception
1454
+     * @return string uid of share owner
1455
+     */
1456
+    private static function getShareOwner(IDBConnection $connection, $shareId) {
1457
+        $qb = $connection->getQueryBuilder();
1458
+
1459
+        $qb->select('uid_owner')
1460
+            ->from('share')
1461
+            ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1462
+            ->setParameter(':shareId', $shareId);
1463
+        $result = $qb->execute();
1464
+        $result = $result->fetch();
1465
+
1466
+        if (empty($result)) {
1467
+            throw new \Exception('Share not found');
1468
+        }
1469
+
1470
+        return $result['uid_owner'];
1471
+    }
1472
+
1473
+    /**
1474
+     * Set password for a public link share
1475
+     *
1476
+     * @param IUserSession $userSession
1477
+     * @param IDBConnection $connection
1478
+     * @param IConfig $config
1479
+     * @param int $shareId
1480
+     * @param string $password
1481
+     * @throws \Exception
1482
+     * @return boolean
1483
+     */
1484
+    public static function setPassword(IUserSession $userSession,
1485
+                                        IDBConnection $connection,
1486
+                                        IConfig $config,
1487
+                                        $shareId, $password) {
1488
+        $user = $userSession->getUser();
1489
+        if (is_null($user)) {
1490
+            throw new \Exception("User not logged in");
1491
+        }
1492
+
1493
+        $uid = self::getShareOwner($connection, $shareId);
1494
+
1495
+        if ($uid !== $user->getUID()) {
1496
+            throw new \Exception('Cannot update share of a different user');
1497
+        }
1498
+
1499
+        if ($password === '') {
1500
+            $password = null;
1501
+        }
1502
+
1503
+        //If passwords are enforced the password can't be null
1504
+        if (self::enforcePassword($config) && is_null($password)) {
1505
+            throw new \Exception('Cannot remove password');
1506
+        }
1507
+
1508
+        self::verifyPassword($password);
1509
+
1510
+        $qb = $connection->getQueryBuilder();
1511
+        $qb->update('share')
1512
+            ->set('share_with', $qb->createParameter('pass'))
1513
+            ->where($qb->expr()->eq('id', $qb->createParameter('shareId')))
1514
+            ->setParameter(':pass', is_null($password) ? null : \OC::$server->getHasher()->hash($password))
1515
+            ->setParameter(':shareId', $shareId);
1516
+
1517
+        $qb->execute();
1518
+
1519
+        return true;
1520
+    }
1521
+
1522
+    /**
1523
+     * Checks whether a share has expired, calls unshareItem() if yes.
1524
+     * @param array $item Share data (usually database row)
1525
+     * @return boolean True if item was expired, false otherwise.
1526
+     */
1527
+    protected static function expireItem(array $item) {
1528
+
1529
+        $result = false;
1530
+
1531
+        // only use default expiration date for link shares
1532
+        if ((int) $item['share_type'] === self::SHARE_TYPE_LINK) {
1533
+
1534
+            // calculate expiration date
1535
+            if (!empty($item['expiration'])) {
1536
+                $userDefinedExpire = new \DateTime($item['expiration']);
1537
+                $expires = $userDefinedExpire->getTimestamp();
1538
+            } else {
1539
+                $expires = null;
1540
+            }
1541
+
1542
+
1543
+            // get default expiration settings
1544
+            $defaultSettings = Helper::getDefaultExpireSetting();
1545
+            $expires = Helper::calculateExpireDate($defaultSettings, $item['stime'], $expires);
1546
+
1547
+
1548
+            if (is_int($expires)) {
1549
+                $now = time();
1550
+                if ($now > $expires) {
1551
+                    self::unshareItem($item);
1552
+                    $result = true;
1553
+                }
1554
+            }
1555
+        }
1556
+        return $result;
1557
+    }
1558
+
1559
+    /**
1560
+     * Unshares a share given a share data array
1561
+     * @param array $item Share data (usually database row)
1562
+     * @param int $newParent parent ID
1563
+     * @return null
1564
+     */
1565
+    protected static function unshareItem(array $item, $newParent = null) {
1566
+
1567
+        $shareType = (int)$item['share_type'];
1568
+        $shareWith = null;
1569
+        if ($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
1570
+            $shareWith = $item['share_with'];
1571
+        }
1572
+
1573
+        // Pass all the vars we have for now, they may be useful
1574
+        $hookParams = array(
1575
+            'id'            => $item['id'],
1576
+            'itemType'      => $item['item_type'],
1577
+            'itemSource'    => $item['item_source'],
1578
+            'shareType'     => $shareType,
1579
+            'shareWith'     => $shareWith,
1580
+            'itemParent'    => $item['parent'],
1581
+            'uidOwner'      => $item['uid_owner'],
1582
+        );
1583
+        if($item['item_type'] === 'file' || $item['item_type'] === 'folder') {
1584
+            $hookParams['fileSource'] = $item['file_source'];
1585
+            $hookParams['fileTarget'] = $item['file_target'];
1586
+        }
1587
+
1588
+        \OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
1589
+        $deletedShares = Helper::delete($item['id'], false, null, $newParent);
1590
+        $deletedShares[] = $hookParams;
1591
+        $hookParams['deletedShares'] = $deletedShares;
1592
+        \OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
1593
+        if ((int)$item['share_type'] === \OCP\Share::SHARE_TYPE_REMOTE && \OC::$server->getUserSession()->getUser()) {
1594
+            list(, $remote) = Helper::splitUserRemote($item['share_with']);
1595
+            self::sendRemoteUnshare($remote, $item['id'], $item['token']);
1596
+        }
1597
+    }
1598
+
1599
+    /**
1600
+     * Get the backend class for the specified item type
1601
+     * @param string $itemType
1602
+     * @throws \Exception
1603
+     * @return \OCP\Share_Backend
1604
+     */
1605
+    public static function getBackend($itemType) {
1606
+        $l = \OC::$server->getL10N('lib');
1607
+        if (isset(self::$backends[$itemType])) {
1608
+            return self::$backends[$itemType];
1609
+        } else if (isset(self::$backendTypes[$itemType]['class'])) {
1610
+            $class = self::$backendTypes[$itemType]['class'];
1611
+            if (class_exists($class)) {
1612
+                self::$backends[$itemType] = new $class;
1613
+                if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) {
1614
+                    $message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
1615
+                    $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', array($class));
1616
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1617
+                    throw new \Exception($message_t);
1618
+                }
1619
+                return self::$backends[$itemType];
1620
+            } else {
1621
+                $message = 'Sharing backend %s not found';
1622
+                $message_t = $l->t('Sharing backend %s not found', array($class));
1623
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $class), \OCP\Util::ERROR);
1624
+                throw new \Exception($message_t);
1625
+            }
1626
+        }
1627
+        $message = 'Sharing backend for %s not found';
1628
+        $message_t = $l->t('Sharing backend for %s not found', array($itemType));
1629
+        \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemType), \OCP\Util::ERROR);
1630
+        throw new \Exception($message_t);
1631
+    }
1632
+
1633
+    /**
1634
+     * Check if resharing is allowed
1635
+     * @return boolean true if allowed or false
1636
+     *
1637
+     * Resharing is allowed by default if not configured
1638
+     */
1639
+    public static function isResharingAllowed() {
1640
+        if (!isset(self::$isResharingAllowed)) {
1641
+            if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
1642
+                self::$isResharingAllowed = true;
1643
+            } else {
1644
+                self::$isResharingAllowed = false;
1645
+            }
1646
+        }
1647
+        return self::$isResharingAllowed;
1648
+    }
1649
+
1650
+    /**
1651
+     * Get a list of collection item types for the specified item type
1652
+     * @param string $itemType
1653
+     * @return array
1654
+     */
1655
+    private static function getCollectionItemTypes($itemType) {
1656
+        $collectionTypes = array($itemType);
1657
+        foreach (self::$backendTypes as $type => $backend) {
1658
+            if (in_array($backend['collectionOf'], $collectionTypes)) {
1659
+                $collectionTypes[] = $type;
1660
+            }
1661
+        }
1662
+        // TODO Add option for collections to be collection of themselves, only 'folder' does it now...
1663
+        if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) {
1664
+            unset($collectionTypes[0]);
1665
+        }
1666
+        // Return array if collections were found or the item type is a
1667
+        // collection itself - collections can be inside collections
1668
+        if (count($collectionTypes) > 0) {
1669
+            return $collectionTypes;
1670
+        }
1671
+        return false;
1672
+    }
1673
+
1674
+    /**
1675
+     * Get the owners of items shared with a user.
1676
+     *
1677
+     * @param string $user The user the items are shared with.
1678
+     * @param string $type The type of the items shared with the user.
1679
+     * @param boolean $includeCollections Include collection item types (optional)
1680
+     * @param boolean $includeOwner include owner in the list of users the item is shared with (optional)
1681
+     * @return array
1682
+     */
1683
+    public static function getSharedItemsOwners($user, $type, $includeCollections = false, $includeOwner = false) {
1684
+        // First, we find out if $type is part of a collection (and if that collection is part of
1685
+        // another one and so on).
1686
+        $collectionTypes = array();
1687
+        if (!$includeCollections || !$collectionTypes = self::getCollectionItemTypes($type)) {
1688
+            $collectionTypes[] = $type;
1689
+        }
1690
+
1691
+        // Of these collection types, along with our original $type, we make a
1692
+        // list of the ones for which a sharing backend has been registered.
1693
+        // FIXME: Ideally, we wouldn't need to nest getItemsSharedWith in this loop but just call it
1694
+        // with its $includeCollections parameter set to true. Unfortunately, this fails currently.
1695
+        $allMaybeSharedItems = array();
1696
+        foreach ($collectionTypes as $collectionType) {
1697
+            if (isset(self::$backends[$collectionType])) {
1698
+                $allMaybeSharedItems[$collectionType] = self::getItemsSharedWithUser(
1699
+                    $collectionType,
1700
+                    $user,
1701
+                    self::FORMAT_NONE
1702
+                );
1703
+            }
1704
+        }
1705
+
1706
+        $owners = array();
1707
+        if ($includeOwner) {
1708
+            $owners[] = $user;
1709
+        }
1710
+
1711
+        // We take a look at all shared items of the given $type (or of the collections it is part of)
1712
+        // and find out their owners. Then, we gather the tags for the original $type from all owners,
1713
+        // and return them as elements of a list that look like "Tag (owner)".
1714
+        foreach ($allMaybeSharedItems as $collectionType => $maybeSharedItems) {
1715
+            foreach ($maybeSharedItems as $sharedItem) {
1716
+                if (isset($sharedItem['id'])) { //workaround for https://github.com/owncloud/core/issues/2814
1717
+                    $owners[] = $sharedItem['uid_owner'];
1718
+                }
1719
+            }
1720
+        }
1721
+
1722
+        return $owners;
1723
+    }
1724
+
1725
+    /**
1726
+     * Get shared items from the database
1727
+     * @param string $itemType
1728
+     * @param string $item Item source or target (optional)
1729
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
1730
+     * @param string $shareWith User or group the item is being shared with
1731
+     * @param string $uidOwner User that is the owner of shared items (optional)
1732
+     * @param int $format Format to convert items to with formatItems() (optional)
1733
+     * @param mixed $parameters to pass to formatItems() (optional)
1734
+     * @param int $limit Number of items to return, -1 to return all matches (optional)
1735
+     * @param boolean $includeCollections Include collection item types (optional)
1736
+     * @param boolean $itemShareWithBySource (optional)
1737
+     * @param boolean $checkExpireDate
1738
+     * @return array
1739
+     *
1740
+     * See public functions getItem(s)... for parameter usage
1741
+     *
1742
+     */
1743
+    public static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
1744
+                                    $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
1745
+                                    $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate  = true) {
1746
+        if (!self::isEnabled()) {
1747
+            return array();
1748
+        }
1749
+        $backend = self::getBackend($itemType);
1750
+        $collectionTypes = false;
1751
+        // Get filesystem root to add it to the file target and remove from the
1752
+        // file source, match file_source with the file cache
1753
+        if ($itemType == 'file' || $itemType == 'folder') {
1754
+            if(!is_null($uidOwner)) {
1755
+                $root = \OC\Files\Filesystem::getRoot();
1756
+            } else {
1757
+                $root = '';
1758
+            }
1759
+            $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid` ';
1760
+            if (!isset($item)) {
1761
+                $where .= ' AND `file_target` IS NOT NULL ';
1762
+            }
1763
+            $where .= 'INNER JOIN `*PREFIX*storages` ON `numeric_id` = `*PREFIX*filecache`.`storage` ';
1764
+            $fileDependent = true;
1765
+            $queryArgs = array();
1766
+        } else {
1767
+            $fileDependent = false;
1768
+            $root = '';
1769
+            $collectionTypes = self::getCollectionItemTypes($itemType);
1770
+            if ($includeCollections && !isset($item) && $collectionTypes) {
1771
+                // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
1772
+                if (!in_array($itemType, $collectionTypes)) {
1773
+                    $itemTypes = array_merge(array($itemType), $collectionTypes);
1774
+                } else {
1775
+                    $itemTypes = $collectionTypes;
1776
+                }
1777
+                $placeholders = join(',', array_fill(0, count($itemTypes), '?'));
1778
+                $where = ' WHERE `item_type` IN ('.$placeholders.'))';
1779
+                $queryArgs = $itemTypes;
1780
+            } else {
1781
+                $where = ' WHERE `item_type` = ?';
1782
+                $queryArgs = array($itemType);
1783
+            }
1784
+        }
1785
+        if (\OC::$server->getAppConfig()->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') {
1786
+            $where .= ' AND `share_type` != ?';
1787
+            $queryArgs[] = self::SHARE_TYPE_LINK;
1788
+        }
1789
+        if (isset($shareType)) {
1790
+            // Include all user and group items
1791
+            if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
1792
+                $where .= ' AND ((`share_type` in (?, ?) AND `share_with` = ?) ';
1793
+                $queryArgs[] = self::SHARE_TYPE_USER;
1794
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
1795
+                $queryArgs[] = $shareWith;
1796
+
1797
+                $user = \OC::$server->getUserManager()->get($shareWith);
1798
+                $groups = [];
1799
+                if ($user) {
1800
+                    $groups = \OC::$server->getGroupManager()->getUserGroupIds($user);
1801
+                }
1802
+                if (!empty($groups)) {
1803
+                    $placeholders = join(',', array_fill(0, count($groups), '?'));
1804
+                    $where .= ' OR (`share_type` = ? AND `share_with` IN ('.$placeholders.')) ';
1805
+                    $queryArgs[] = self::SHARE_TYPE_GROUP;
1806
+                    $queryArgs = array_merge($queryArgs, $groups);
1807
+                }
1808
+                $where .= ')';
1809
+                // Don't include own group shares
1810
+                $where .= ' AND `uid_owner` != ?';
1811
+                $queryArgs[] = $shareWith;
1812
+            } else {
1813
+                $where .= ' AND `share_type` = ?';
1814
+                $queryArgs[] = $shareType;
1815
+                if (isset($shareWith)) {
1816
+                    $where .= ' AND `share_with` = ?';
1817
+                    $queryArgs[] = $shareWith;
1818
+                }
1819
+            }
1820
+        }
1821
+        if (isset($uidOwner)) {
1822
+            $where .= ' AND `uid_owner` = ?';
1823
+            $queryArgs[] = $uidOwner;
1824
+            if (!isset($shareType)) {
1825
+                // Prevent unique user targets for group shares from being selected
1826
+                $where .= ' AND `share_type` != ?';
1827
+                $queryArgs[] = self::$shareTypeGroupUserUnique;
1828
+            }
1829
+            if ($fileDependent) {
1830
+                $column = 'file_source';
1831
+            } else {
1832
+                $column = 'item_source';
1833
+            }
1834
+        } else {
1835
+            if ($fileDependent) {
1836
+                $column = 'file_target';
1837
+            } else {
1838
+                $column = 'item_target';
1839
+            }
1840
+        }
1841
+        if (isset($item)) {
1842
+            $collectionTypes = self::getCollectionItemTypes($itemType);
1843
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1844
+                $where .= ' AND (';
1845
+            } else {
1846
+                $where .= ' AND';
1847
+            }
1848
+            // If looking for own shared items, check item_source else check item_target
1849
+            if (isset($uidOwner) || $itemShareWithBySource) {
1850
+                // If item type is a file, file source needs to be checked in case the item was converted
1851
+                if ($fileDependent) {
1852
+                    $where .= ' `file_source` = ?';
1853
+                    $column = 'file_source';
1854
+                } else {
1855
+                    $where .= ' `item_source` = ?';
1856
+                    $column = 'item_source';
1857
+                }
1858
+            } else {
1859
+                if ($fileDependent) {
1860
+                    $where .= ' `file_target` = ?';
1861
+                    $item = \OC\Files\Filesystem::normalizePath($item);
1862
+                } else {
1863
+                    $where .= ' `item_target` = ?';
1864
+                }
1865
+            }
1866
+            $queryArgs[] = $item;
1867
+            if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) {
1868
+                $placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
1869
+                $where .= ' OR `item_type` IN ('.$placeholders.'))';
1870
+                $queryArgs = array_merge($queryArgs, $collectionTypes);
1871
+            }
1872
+        }
1873
+
1874
+        if ($shareType == self::$shareTypeUserAndGroups && $limit === 1) {
1875
+            // Make sure the unique user target is returned if it exists,
1876
+            // unique targets should follow the group share in the database
1877
+            // If the limit is not 1, the filtering can be done later
1878
+            $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
1879
+        } else {
1880
+            $where .= ' ORDER BY `*PREFIX*share`.`id` ASC';
1881
+        }
1882
+
1883
+        if ($limit != -1 && !$includeCollections) {
1884
+            // The limit must be at least 3, because filtering needs to be done
1885
+            if ($limit < 3) {
1886
+                $queryLimit = 3;
1887
+            } else {
1888
+                $queryLimit = $limit;
1889
+            }
1890
+        } else {
1891
+            $queryLimit = null;
1892
+        }
1893
+        $select = self::createSelectStatement($format, $fileDependent, $uidOwner);
1894
+        $root = strlen($root);
1895
+        $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
1896
+        $result = $query->execute($queryArgs);
1897
+        if ($result === false) {
1898
+            \OCP\Util::writeLog('OCP\Share',
1899
+                \OC_DB::getErrorMessage() . ', select=' . $select . ' where=',
1900
+                \OCP\Util::ERROR);
1901
+        }
1902
+        $items = array();
1903
+        $targets = array();
1904
+        $switchedItems = array();
1905
+        $mounts = array();
1906
+        while ($row = $result->fetchRow()) {
1907
+            self::transformDBResults($row);
1908
+            // Filter out duplicate group shares for users with unique targets
1909
+            if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) {
1910
+                continue;
1911
+            }
1912
+            if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
1913
+                $row['share_type'] = self::SHARE_TYPE_GROUP;
1914
+                $row['unique_name'] = true; // remember that we use a unique name for this user
1915
+                $row['share_with'] = $items[$row['parent']]['share_with'];
1916
+                // if the group share was unshared from the user we keep the permission, otherwise
1917
+                // we take the permission from the parent because this is always the up-to-date
1918
+                // permission for the group share
1919
+                if ($row['permissions'] > 0) {
1920
+                    $row['permissions'] = $items[$row['parent']]['permissions'];
1921
+                }
1922
+                // Remove the parent group share
1923
+                unset($items[$row['parent']]);
1924
+                if ($row['permissions'] == 0) {
1925
+                    continue;
1926
+                }
1927
+            } else if (!isset($uidOwner)) {
1928
+                // Check if the same target already exists
1929
+                if (isset($targets[$row['id']])) {
1930
+                    // Check if the same owner shared with the user twice
1931
+                    // through a group and user share - this is allowed
1932
+                    $id = $targets[$row['id']];
1933
+                    if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
1934
+                        // Switch to group share type to ensure resharing conditions aren't bypassed
1935
+                        if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
1936
+                            $items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
1937
+                            $items[$id]['share_with'] = $row['share_with'];
1938
+                        }
1939
+                        // Switch ids if sharing permission is granted on only
1940
+                        // one share to ensure correct parent is used if resharing
1941
+                        if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE
1942
+                            && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
1943
+                            $items[$row['id']] = $items[$id];
1944
+                            $switchedItems[$id] = $row['id'];
1945
+                            unset($items[$id]);
1946
+                            $id = $row['id'];
1947
+                        }
1948
+                        $items[$id]['permissions'] |= (int)$row['permissions'];
1949
+
1950
+                    }
1951
+                    continue;
1952
+                } elseif (!empty($row['parent'])) {
1953
+                    $targets[$row['parent']] = $row['id'];
1954
+                }
1955
+            }
1956
+            // Remove root from file source paths if retrieving own shared items
1957
+            if (isset($uidOwner) && isset($row['path'])) {
1958
+                if (isset($row['parent'])) {
1959
+                    $query = \OC_DB::prepare('SELECT `file_target` FROM `*PREFIX*share` WHERE `id` = ?');
1960
+                    $parentResult = $query->execute(array($row['parent']));
1961
+                    if ($result === false) {
1962
+                        \OCP\Util::writeLog('OCP\Share', 'Can\'t select parent: ' .
1963
+                            \OC_DB::getErrorMessage() . ', select=' . $select . ' where=' . $where,
1964
+                            \OCP\Util::ERROR);
1965
+                    } else {
1966
+                        $parentRow = $parentResult->fetchRow();
1967
+                        $tmpPath = $parentRow['file_target'];
1968
+                        // find the right position where the row path continues from the target path
1969
+                        $pos = strrpos($row['path'], $parentRow['file_target']);
1970
+                        $subPath = substr($row['path'], $pos);
1971
+                        $splitPath = explode('/', $subPath);
1972
+                        foreach (array_slice($splitPath, 2) as $pathPart) {
1973
+                            $tmpPath = $tmpPath . '/' . $pathPart;
1974
+                        }
1975
+                        $row['path'] = $tmpPath;
1976
+                    }
1977
+                } else {
1978
+                    if (!isset($mounts[$row['storage']])) {
1979
+                        $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
1980
+                        if (is_array($mountPoints) && !empty($mountPoints)) {
1981
+                            $mounts[$row['storage']] = current($mountPoints);
1982
+                        }
1983
+                    }
1984
+                    if (!empty($mounts[$row['storage']])) {
1985
+                        $path = $mounts[$row['storage']]->getMountPoint().$row['path'];
1986
+                        $relPath = substr($path, $root); // path relative to data/user
1987
+                        $row['path'] = rtrim($relPath, '/');
1988
+                    }
1989
+                }
1990
+            }
1991
+
1992
+            if($checkExpireDate) {
1993
+                if (self::expireItem($row)) {
1994
+                    continue;
1995
+                }
1996
+            }
1997
+            // Check if resharing is allowed, if not remove share permission
1998
+            if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) {
1999
+                $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE;
2000
+            }
2001
+            // Add display names to result
2002
+            $row['share_with_displayname'] = $row['share_with'];
2003
+            if ( isset($row['share_with']) && $row['share_with'] != '' &&
2004
+                $row['share_type'] === self::SHARE_TYPE_USER) {
2005
+                $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
2006
+            } else if(isset($row['share_with']) && $row['share_with'] != '' &&
2007
+                $row['share_type'] === self::SHARE_TYPE_REMOTE) {
2008
+                $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
2009
+                foreach ($addressBookEntries as $entry) {
2010
+                    foreach ($entry['CLOUD'] as $cloudID) {
2011
+                        if ($cloudID === $row['share_with']) {
2012
+                            $row['share_with_displayname'] = $entry['FN'];
2013
+                        }
2014
+                    }
2015
+                }
2016
+            }
2017
+            if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
2018
+                $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
2019
+            }
2020
+
2021
+            if ($row['permissions'] > 0) {
2022
+                $items[$row['id']] = $row;
2023
+            }
2024
+
2025
+        }
2026
+
2027
+        // group items if we are looking for items shared with the current user
2028
+        if (isset($shareWith) && $shareWith === \OCP\User::getUser()) {
2029
+            $items = self::groupItems($items, $itemType);
2030
+        }
2031
+
2032
+        if (!empty($items)) {
2033
+            $collectionItems = array();
2034
+            foreach ($items as &$row) {
2035
+                // Return only the item instead of a 2-dimensional array
2036
+                if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
2037
+                    if ($format == self::FORMAT_NONE) {
2038
+                        return $row;
2039
+                    } else {
2040
+                        break;
2041
+                    }
2042
+                }
2043
+                // Check if this is a collection of the requested item type
2044
+                if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) {
2045
+                    if (($collectionBackend = self::getBackend($row['item_type']))
2046
+                        && $collectionBackend instanceof \OCP\Share_Backend_Collection) {
2047
+                        // Collections can be inside collections, check if the item is a collection
2048
+                        if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
2049
+                            $collectionItems[] = $row;
2050
+                        } else {
2051
+                            $collection = array();
2052
+                            $collection['item_type'] = $row['item_type'];
2053
+                            if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2054
+                                $collection['path'] = basename($row['path']);
2055
+                            }
2056
+                            $row['collection'] = $collection;
2057
+                            // Fetch all of the children sources
2058
+                            $children = $collectionBackend->getChildren($row[$column]);
2059
+                            foreach ($children as $child) {
2060
+                                $childItem = $row;
2061
+                                $childItem['item_type'] = $itemType;
2062
+                                if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
2063
+                                    $childItem['item_source'] = $child['source'];
2064
+                                    $childItem['item_target'] = $child['target'];
2065
+                                }
2066
+                                if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2067
+                                    if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
2068
+                                        $childItem['file_source'] = $child['source'];
2069
+                                    } else { // TODO is this really needed if we already know that we use the file backend?
2070
+                                        $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
2071
+                                        $childItem['file_source'] = $meta['fileid'];
2072
+                                    }
2073
+                                    $childItem['file_target'] =
2074
+                                        \OC\Files\Filesystem::normalizePath($child['file_path']);
2075
+                                }
2076
+                                if (isset($item)) {
2077
+                                    if ($childItem[$column] == $item) {
2078
+                                        // Return only the item instead of a 2-dimensional array
2079
+                                        if ($limit == 1) {
2080
+                                            if ($format == self::FORMAT_NONE) {
2081
+                                                return $childItem;
2082
+                                            } else {
2083
+                                                // Unset the items array and break out of both loops
2084
+                                                $items = array();
2085
+                                                $items[] = $childItem;
2086
+                                                break 2;
2087
+                                            }
2088
+                                        } else {
2089
+                                            $collectionItems[] = $childItem;
2090
+                                        }
2091
+                                    }
2092
+                                } else {
2093
+                                    $collectionItems[] = $childItem;
2094
+                                }
2095
+                            }
2096
+                        }
2097
+                    }
2098
+                    // Remove collection item
2099
+                    $toRemove = $row['id'];
2100
+                    if (array_key_exists($toRemove, $switchedItems)) {
2101
+                        $toRemove = $switchedItems[$toRemove];
2102
+                    }
2103
+                    unset($items[$toRemove]);
2104
+                } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
2105
+                    // FIXME: Thats a dirty hack to improve file sharing performance,
2106
+                    // see github issue #10588 for more details
2107
+                    // Need to find a solution which works for all back-ends
2108
+                    $collectionBackend = self::getBackend($row['item_type']);
2109
+                    $sharedParents = $collectionBackend->getParents($row['item_source']);
2110
+                    foreach ($sharedParents as $parent) {
2111
+                        $collectionItems[] = $parent;
2112
+                    }
2113
+                }
2114
+            }
2115
+            if (!empty($collectionItems)) {
2116
+                $collectionItems = array_unique($collectionItems, SORT_REGULAR);
2117
+                $items = array_merge($items, $collectionItems);
2118
+            }
2119
+
2120
+            // filter out invalid items, these can appear when subshare entries exist
2121
+            // for a group in which the requested user isn't a member any more
2122
+            $items = array_filter($items, function($item) {
2123
+                return $item['share_type'] !== self::$shareTypeGroupUserUnique;
2124
+            });
2125
+
2126
+            return self::formatResult($items, $column, $backend, $format, $parameters);
2127
+        } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) {
2128
+            // FIXME: Thats a dirty hack to improve file sharing performance,
2129
+            // see github issue #10588 for more details
2130
+            // Need to find a solution which works for all back-ends
2131
+            $collectionItems = array();
2132
+            $collectionBackend = self::getBackend('folder');
2133
+            $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner);
2134
+            foreach ($sharedParents as $parent) {
2135
+                $collectionItems[] = $parent;
2136
+            }
2137
+            if ($limit === 1) {
2138
+                return reset($collectionItems);
2139
+            }
2140
+            return self::formatResult($collectionItems, $column, $backend, $format, $parameters);
2141
+        }
2142
+
2143
+        return array();
2144
+    }
2145
+
2146
+    /**
2147
+     * group items with link to the same source
2148
+     *
2149
+     * @param array $items
2150
+     * @param string $itemType
2151
+     * @return array of grouped items
2152
+     */
2153
+    protected static function groupItems($items, $itemType) {
2154
+
2155
+        $fileSharing = ($itemType === 'file' || $itemType === 'folder') ? true : false;
2156
+
2157
+        $result = array();
2158
+
2159
+        foreach ($items as $item) {
2160
+            $grouped = false;
2161
+            foreach ($result as $key => $r) {
2162
+                // for file/folder shares we need to compare file_source, otherwise we compare item_source
2163
+                // only group shares if they already point to the same target, otherwise the file where shared
2164
+                // before grouping of shares was added. In this case we don't group them toi avoid confusions
2165
+                if (( $fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
2166
+                    (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
2167
+                    // add the first item to the list of grouped shares
2168
+                    if (!isset($result[$key]['grouped'])) {
2169
+                        $result[$key]['grouped'][] = $result[$key];
2170
+                    }
2171
+                    $result[$key]['permissions'] = (int) $item['permissions'] | (int) $r['permissions'];
2172
+                    $result[$key]['grouped'][] = $item;
2173
+                    $grouped = true;
2174
+                    break;
2175
+                }
2176
+            }
2177
+
2178
+            if (!$grouped) {
2179
+                $result[] = $item;
2180
+            }
2181
+
2182
+        }
2183
+
2184
+        return $result;
2185
+    }
2186
+
2187
+    /**
2188
+     * Put shared item into the database
2189
+     * @param string $itemType Item type
2190
+     * @param string $itemSource Item source
2191
+     * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
2192
+     * @param string $shareWith User or group the item is being shared with
2193
+     * @param string $uidOwner User that is the owner of shared item
2194
+     * @param int $permissions CRUDS permissions
2195
+     * @param boolean|array $parentFolder Parent folder target (optional)
2196
+     * @param string $token (optional)
2197
+     * @param string $itemSourceName name of the source item (optional)
2198
+     * @param \DateTime $expirationDate (optional)
2199
+     * @throws \Exception
2200
+     * @return mixed id of the new share or false
2201
+     */
2202
+    private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2203
+                                $permissions, $parentFolder = null, $token = null, $itemSourceName = null, \DateTime $expirationDate = null) {
2204
+
2205
+        $queriesToExecute = array();
2206
+        $suggestedItemTarget = null;
2207
+        $groupFileTarget = $fileTarget = $suggestedFileTarget = $filePath = '';
2208
+        $groupItemTarget = $itemTarget = $fileSource = $parent = 0;
2209
+
2210
+        $result = self::checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate);
2211
+        if(!empty($result)) {
2212
+            $parent = $result['parent'];
2213
+            $itemSource = $result['itemSource'];
2214
+            $fileSource = $result['fileSource'];
2215
+            $suggestedItemTarget = $result['suggestedItemTarget'];
2216
+            $suggestedFileTarget = $result['suggestedFileTarget'];
2217
+            $filePath = $result['filePath'];
2218
+        }
2219
+
2220
+        $isGroupShare = false;
2221
+        if ($shareType == self::SHARE_TYPE_GROUP) {
2222
+            $isGroupShare = true;
2223
+            if (isset($shareWith['users'])) {
2224
+                $users = $shareWith['users'];
2225
+            } else {
2226
+                $group = \OC::$server->getGroupManager()->get($shareWith['group']);
2227
+                if ($group) {
2228
+                    $users = $group->searchUsers('', -1, 0);
2229
+                    $userIds = [];
2230
+                    foreach ($users as $user) {
2231
+                        $userIds[] = $user->getUID();
2232
+                    }
2233
+                    $users = $userIds;
2234
+                } else {
2235
+                    $users = [];
2236
+                }
2237
+            }
2238
+            // remove current user from list
2239
+            if (in_array(\OCP\User::getUser(), $users)) {
2240
+                unset($users[array_search(\OCP\User::getUser(), $users)]);
2241
+            }
2242
+            $groupItemTarget = Helper::generateTarget($itemType, $itemSource,
2243
+                $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
2244
+            $groupFileTarget = Helper::generateTarget($itemType, $itemSource,
2245
+                $shareType, $shareWith['group'], $uidOwner, $filePath);
2246
+
2247
+            // add group share to table and remember the id as parent
2248
+            $queriesToExecute['groupShare'] = array(
2249
+                'itemType'			=> $itemType,
2250
+                'itemSource'		=> $itemSource,
2251
+                'itemTarget'		=> $groupItemTarget,
2252
+                'shareType'			=> $shareType,
2253
+                'shareWith'			=> $shareWith['group'],
2254
+                'uidOwner'			=> $uidOwner,
2255
+                'permissions'		=> $permissions,
2256
+                'shareTime'			=> time(),
2257
+                'fileSource'		=> $fileSource,
2258
+                'fileTarget'		=> $groupFileTarget,
2259
+                'token'				=> $token,
2260
+                'parent'			=> $parent,
2261
+                'expiration'		=> $expirationDate,
2262
+            );
2263
+
2264
+        } else {
2265
+            $users = array($shareWith);
2266
+            $itemTarget = Helper::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
2267
+                $suggestedItemTarget);
2268
+        }
2269
+
2270
+        $run = true;
2271
+        $error = '';
2272
+        $preHookData = array(
2273
+            'itemType' => $itemType,
2274
+            'itemSource' => $itemSource,
2275
+            'shareType' => $shareType,
2276
+            'uidOwner' => $uidOwner,
2277
+            'permissions' => $permissions,
2278
+            'fileSource' => $fileSource,
2279
+            'expiration' => $expirationDate,
2280
+            'token' => $token,
2281
+            'run' => &$run,
2282
+            'error' => &$error
2283
+        );
2284
+
2285
+        $preHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2286
+        $preHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2287
+
2288
+        \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
2289
+
2290
+        if ($run === false) {
2291
+            throw new \Exception($error);
2292
+        }
2293
+
2294
+        foreach ($users as $user) {
2295
+            $sourceId = ($itemType === 'file' || $itemType === 'folder') ? $fileSource : $itemSource;
2296
+            $sourceExists = self::getItemSharedWithBySource($itemType, $sourceId, self::FORMAT_NONE, null, true, $user);
2297
+
2298
+            $userShareType = ($isGroupShare) ? self::$shareTypeGroupUserUnique : $shareType;
2299
+
2300
+            if ($sourceExists && $sourceExists['item_source'] === $itemSource) {
2301
+                $fileTarget = $sourceExists['file_target'];
2302
+                $itemTarget = $sourceExists['item_target'];
2303
+
2304
+                // for group shares we don't need a additional entry if the target is the same
2305
+                if($isGroupShare && $groupItemTarget === $itemTarget) {
2306
+                    continue;
2307
+                }
2308
+
2309
+            } elseif(!$sourceExists && !$isGroupShare)  {
2310
+
2311
+                $itemTarget = Helper::generateTarget($itemType, $itemSource, $userShareType, $user,
2312
+                    $uidOwner, $suggestedItemTarget, $parent);
2313
+                if (isset($fileSource)) {
2314
+                    if ($parentFolder) {
2315
+                        if ($parentFolder === true) {
2316
+                            $fileTarget = Helper::generateTarget('file', $filePath, $userShareType, $user,
2317
+                                $uidOwner, $suggestedFileTarget, $parent);
2318
+                            if ($fileTarget != $groupFileTarget) {
2319
+                                $parentFolders[$user]['folder'] = $fileTarget;
2320
+                            }
2321
+                        } else if (isset($parentFolder[$user])) {
2322
+                            $fileTarget = $parentFolder[$user]['folder'].$itemSource;
2323
+                            $parent = $parentFolder[$user]['id'];
2324
+                        }
2325
+                    } else {
2326
+                        $fileTarget = Helper::generateTarget('file', $filePath, $userShareType,
2327
+                            $user, $uidOwner, $suggestedFileTarget, $parent);
2328
+                    }
2329
+                } else {
2330
+                    $fileTarget = null;
2331
+                }
2332
+
2333
+            } else {
2334
+
2335
+                // group share which doesn't exists until now, check if we need a unique target for this user
2336
+
2337
+                $itemTarget = Helper::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $user,
2338
+                    $uidOwner, $suggestedItemTarget, $parent);
2339
+
2340
+                // do we also need a file target
2341
+                if (isset($fileSource)) {
2342
+                    $fileTarget = Helper::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $user,
2343
+                        $uidOwner, $suggestedFileTarget, $parent);
2344
+                } else {
2345
+                    $fileTarget = null;
2346
+                }
2347
+
2348
+                if (($itemTarget === $groupItemTarget) &&
2349
+                    (!isset($fileSource) || $fileTarget === $groupFileTarget)) {
2350
+                    continue;
2351
+                }
2352
+            }
2353
+
2354
+            $queriesToExecute[] = array(
2355
+                'itemType'			=> $itemType,
2356
+                'itemSource'		=> $itemSource,
2357
+                'itemTarget'		=> $itemTarget,
2358
+                'shareType'			=> $userShareType,
2359
+                'shareWith'			=> $user,
2360
+                'uidOwner'			=> $uidOwner,
2361
+                'permissions'		=> $permissions,
2362
+                'shareTime'			=> time(),
2363
+                'fileSource'		=> $fileSource,
2364
+                'fileTarget'		=> $fileTarget,
2365
+                'token'				=> $token,
2366
+                'parent'			=> $parent,
2367
+                'expiration'		=> $expirationDate,
2368
+            );
2369
+
2370
+        }
2371
+
2372
+        $id = false;
2373
+        if ($isGroupShare) {
2374
+            $id = self::insertShare($queriesToExecute['groupShare']);
2375
+            // Save this id, any extra rows for this group share will need to reference it
2376
+            $parent = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2377
+            unset($queriesToExecute['groupShare']);
2378
+        }
2379
+
2380
+        foreach ($queriesToExecute as $shareQuery) {
2381
+            $shareQuery['parent'] = $parent;
2382
+            $id = self::insertShare($shareQuery);
2383
+        }
2384
+
2385
+        $postHookData = array(
2386
+            'itemType' => $itemType,
2387
+            'itemSource' => $itemSource,
2388
+            'parent' => $parent,
2389
+            'shareType' => $shareType,
2390
+            'uidOwner' => $uidOwner,
2391
+            'permissions' => $permissions,
2392
+            'fileSource' => $fileSource,
2393
+            'id' => $parent,
2394
+            'token' => $token,
2395
+            'expirationDate' => $expirationDate,
2396
+        );
2397
+
2398
+        $postHookData['shareWith'] = ($isGroupShare) ? $shareWith['group'] : $shareWith;
2399
+        $postHookData['itemTarget'] = ($isGroupShare) ? $groupItemTarget : $itemTarget;
2400
+        $postHookData['fileTarget'] = ($isGroupShare) ? $groupFileTarget : $fileTarget;
2401
+
2402
+        \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
2403
+
2404
+
2405
+        return $id ? $id : false;
2406
+    }
2407
+
2408
+    /**
2409
+     * @param string $itemType
2410
+     * @param string $itemSource
2411
+     * @param int $shareType
2412
+     * @param string $shareWith
2413
+     * @param string $uidOwner
2414
+     * @param int $permissions
2415
+     * @param string|null $itemSourceName
2416
+     * @param null|\DateTime $expirationDate
2417
+     */
2418
+    private static function checkReshare($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $itemSourceName, $expirationDate) {
2419
+        $backend = self::getBackend($itemType);
2420
+
2421
+        $l = \OC::$server->getL10N('lib');
2422
+        $result = array();
2423
+
2424
+        $column = ($itemType === 'file' || $itemType === 'folder') ? 'file_source' : 'item_source';
2425
+
2426
+        $checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true);
2427
+        if ($checkReshare) {
2428
+            // Check if attempting to share back to owner
2429
+            if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
2430
+                $message = 'Sharing %s failed, because the user %s is the original sharer';
2431
+                $message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
2432
+
2433
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
2434
+                throw new \Exception($message_t);
2435
+            }
2436
+        }
2437
+
2438
+        if ($checkReshare && $checkReshare['uid_owner'] !== \OC_User::getUser()) {
2439
+            // Check if share permissions is granted
2440
+            if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & \OCP\Constants::PERMISSION_SHARE) {
2441
+                if (~(int)$checkReshare['permissions'] & $permissions) {
2442
+                    $message = 'Sharing %s failed, because the permissions exceed permissions granted to %s';
2443
+                    $message_t = $l->t('Sharing %s failed, because the permissions exceed permissions granted to %s', array($itemSourceName, $uidOwner));
2444
+
2445
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $uidOwner), \OCP\Util::DEBUG);
2446
+                    throw new \Exception($message_t);
2447
+                } else {
2448
+                    // TODO Don't check if inside folder
2449
+                    $result['parent'] = $checkReshare['id'];
2450
+
2451
+                    $result['expirationDate'] = $expirationDate;
2452
+                    // $checkReshare['expiration'] could be null and then is always less than any value
2453
+                    if(isset($checkReshare['expiration']) && $checkReshare['expiration'] < $expirationDate) {
2454
+                        $result['expirationDate'] = $checkReshare['expiration'];
2455
+                    }
2456
+
2457
+                    // only suggest the same name as new target if it is a reshare of the
2458
+                    // same file/folder and not the reshare of a child
2459
+                    if ($checkReshare[$column] === $itemSource) {
2460
+                        $result['filePath'] = $checkReshare['file_target'];
2461
+                        $result['itemSource'] = $checkReshare['item_source'];
2462
+                        $result['fileSource'] = $checkReshare['file_source'];
2463
+                        $result['suggestedItemTarget'] = $checkReshare['item_target'];
2464
+                        $result['suggestedFileTarget'] = $checkReshare['file_target'];
2465
+                    } else {
2466
+                        $result['filePath'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $backend->getFilePath($itemSource, $uidOwner) : null;
2467
+                        $result['suggestedItemTarget'] = null;
2468
+                        $result['suggestedFileTarget'] = null;
2469
+                        $result['itemSource'] = $itemSource;
2470
+                        $result['fileSource'] = ($backend instanceof \OCP\Share_Backend_File_Dependent) ? $itemSource : null;
2471
+                    }
2472
+                }
2473
+            } else {
2474
+                $message = 'Sharing %s failed, because resharing is not allowed';
2475
+                $message_t = $l->t('Sharing %s failed, because resharing is not allowed', array($itemSourceName));
2476
+
2477
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
2478
+                throw new \Exception($message_t);
2479
+            }
2480
+        } else {
2481
+            $result['parent'] = null;
2482
+            $result['suggestedItemTarget'] = null;
2483
+            $result['suggestedFileTarget'] = null;
2484
+            $result['itemSource'] = $itemSource;
2485
+            $result['expirationDate'] = $expirationDate;
2486
+            if (!$backend->isValidSource($itemSource, $uidOwner)) {
2487
+                $message = 'Sharing %s failed, because the sharing backend for '
2488
+                    .'%s could not find its source';
2489
+                $message_t = $l->t('Sharing %s failed, because the sharing backend for %s could not find its source', array($itemSource, $itemType));
2490
+                \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource, $itemType), \OCP\Util::DEBUG);
2491
+                throw new \Exception($message_t);
2492
+            }
2493
+            if ($backend instanceof \OCP\Share_Backend_File_Dependent) {
2494
+                $result['filePath'] = $backend->getFilePath($itemSource, $uidOwner);
2495
+                if ($itemType == 'file' || $itemType == 'folder') {
2496
+                    $result['fileSource'] = $itemSource;
2497
+                } else {
2498
+                    $meta = \OC\Files\Filesystem::getFileInfo($result['filePath']);
2499
+                    $result['fileSource'] = $meta['fileid'];
2500
+                }
2501
+                if ($result['fileSource'] == -1) {
2502
+                    $message = 'Sharing %s failed, because the file could not be found in the file cache';
2503
+                    $message_t = $l->t('Sharing %s failed, because the file could not be found in the file cache', array($itemSource));
2504
+
2505
+                    \OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSource), \OCP\Util::DEBUG);
2506
+                    throw new \Exception($message_t);
2507
+                }
2508
+            } else {
2509
+                $result['filePath'] = null;
2510
+                $result['fileSource'] = null;
2511
+            }
2512
+        }
2513
+
2514
+        return $result;
2515
+    }
2516
+
2517
+    /**
2518
+     *
2519
+     * @param array $shareData
2520
+     * @return mixed false in case of a failure or the id of the new share
2521
+     */
2522
+    private static function insertShare(array $shareData) {
2523
+
2524
+        $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` ('
2525
+            .' `item_type`, `item_source`, `item_target`, `share_type`,'
2526
+            .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
2527
+            .' `file_target`, `token`, `parent`, `expiration`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)');
2528
+        $query->bindValue(1, $shareData['itemType']);
2529
+        $query->bindValue(2, $shareData['itemSource']);
2530
+        $query->bindValue(3, $shareData['itemTarget']);
2531
+        $query->bindValue(4, $shareData['shareType']);
2532
+        $query->bindValue(5, $shareData['shareWith']);
2533
+        $query->bindValue(6, $shareData['uidOwner']);
2534
+        $query->bindValue(7, $shareData['permissions']);
2535
+        $query->bindValue(8, $shareData['shareTime']);
2536
+        $query->bindValue(9, $shareData['fileSource']);
2537
+        $query->bindValue(10, $shareData['fileTarget']);
2538
+        $query->bindValue(11, $shareData['token']);
2539
+        $query->bindValue(12, $shareData['parent']);
2540
+        $query->bindValue(13, $shareData['expiration'], 'datetime');
2541
+        $result = $query->execute();
2542
+
2543
+        $id = false;
2544
+        if ($result) {
2545
+            $id =  \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share');
2546
+        }
2547
+
2548
+        return $id;
2549
+
2550
+    }
2551
+
2552
+    /**
2553
+     * Delete all shares with type SHARE_TYPE_LINK
2554
+     */
2555
+    public static function removeAllLinkShares() {
2556
+        // Delete any link shares
2557
+        $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `share_type` = ?');
2558
+        $result = $query->execute(array(self::SHARE_TYPE_LINK));
2559
+        while ($item = $result->fetchRow()) {
2560
+            Helper::delete($item['id']);
2561
+        }
2562
+    }
2563
+
2564
+    /**
2565
+     * In case a password protected link is not yet authenticated this function will return false
2566
+     *
2567
+     * @param array $linkItem
2568
+     * @return boolean
2569
+     */
2570
+    public static function checkPasswordProtectedShare(array $linkItem) {
2571
+        if (!isset($linkItem['share_with'])) {
2572
+            return true;
2573
+        }
2574
+        if (!isset($linkItem['share_type'])) {
2575
+            return true;
2576
+        }
2577
+        if (!isset($linkItem['id'])) {
2578
+            return true;
2579
+        }
2580
+
2581
+        if ($linkItem['share_type'] != \OCP\Share::SHARE_TYPE_LINK) {
2582
+            return true;
2583
+        }
2584
+
2585
+        if ( \OC::$server->getSession()->exists('public_link_authenticated')
2586
+            && \OC::$server->getSession()->get('public_link_authenticated') === (string)$linkItem['id'] ) {
2587
+            return true;
2588
+        }
2589
+
2590
+        return false;
2591
+    }
2592
+
2593
+    /**
2594
+     * construct select statement
2595
+     * @param int $format
2596
+     * @param boolean $fileDependent ist it a file/folder share or a generla share
2597
+     * @param string $uidOwner
2598
+     * @return string select statement
2599
+     */
2600
+    private static function createSelectStatement($format, $fileDependent, $uidOwner = null) {
2601
+        $select = '*';
2602
+        if ($format == self::FORMAT_STATUSES) {
2603
+            if ($fileDependent) {
2604
+                $select = '`*PREFIX*share`.`id`, `*PREFIX*share`.`parent`, `share_type`, `path`, `storage`, '
2605
+                    . '`share_with`, `uid_owner` , `file_source`, `stime`, `*PREFIX*share`.`permissions`, '
2606
+                    . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2607
+                    . '`uid_initiator`';
2608
+            } else {
2609
+                $select = '`id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_source`, `stime`, `*PREFIX*share`.`permissions`';
2610
+            }
2611
+        } else {
2612
+            if (isset($uidOwner)) {
2613
+                if ($fileDependent) {
2614
+                    $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`,'
2615
+                        . ' `share_type`, `share_with`, `file_source`, `file_target`, `path`, `*PREFIX*share`.`permissions`, `stime`,'
2616
+                        . ' `expiration`, `token`, `storage`, `mail_send`, `uid_owner`, '
2617
+                        . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2618
+                } else {
2619
+                    $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `*PREFIX*share`.`permissions`,'
2620
+                        . ' `stime`, `file_source`, `expiration`, `token`, `mail_send`, `uid_owner`';
2621
+                }
2622
+            } else {
2623
+                if ($fileDependent) {
2624
+                    if ($format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_GET_FOLDER_CONTENTS || $format == \OCA\Files_Sharing\ShareBackend\File::FORMAT_FILE_APP_ROOT) {
2625
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `*PREFIX*share`.`parent`, `uid_owner`, '
2626
+                            . '`share_type`, `share_with`, `file_source`, `path`, `file_target`, `stime`, '
2627
+                            . '`*PREFIX*share`.`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
2628
+                            . '`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`';
2629
+                    } else {
2630
+                        $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,'
2631
+                            . '`*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,'
2632
+                            . '`file_source`, `path`, `file_target`, `*PREFIX*share`.`permissions`,'
2633
+                            . '`stime`, `expiration`, `token`, `storage`, `mail_send`,'
2634
+                            . '`*PREFIX*storages`.`id` AS `storage_id`, `*PREFIX*filecache`.`parent` as `file_parent`';
2635
+                    }
2636
+                }
2637
+            }
2638
+        }
2639
+        return $select;
2640
+    }
2641
+
2642
+
2643
+    /**
2644
+     * transform db results
2645
+     * @param array $row result
2646
+     */
2647
+    private static function transformDBResults(&$row) {
2648
+        if (isset($row['id'])) {
2649
+            $row['id'] = (int) $row['id'];
2650
+        }
2651
+        if (isset($row['share_type'])) {
2652
+            $row['share_type'] = (int) $row['share_type'];
2653
+        }
2654
+        if (isset($row['parent'])) {
2655
+            $row['parent'] = (int) $row['parent'];
2656
+        }
2657
+        if (isset($row['file_parent'])) {
2658
+            $row['file_parent'] = (int) $row['file_parent'];
2659
+        }
2660
+        if (isset($row['file_source'])) {
2661
+            $row['file_source'] = (int) $row['file_source'];
2662
+        }
2663
+        if (isset($row['permissions'])) {
2664
+            $row['permissions'] = (int) $row['permissions'];
2665
+        }
2666
+        if (isset($row['storage'])) {
2667
+            $row['storage'] = (int) $row['storage'];
2668
+        }
2669
+        if (isset($row['stime'])) {
2670
+            $row['stime'] = (int) $row['stime'];
2671
+        }
2672
+        if (isset($row['expiration']) && $row['share_type'] !== self::SHARE_TYPE_LINK) {
2673
+            // discard expiration date for non-link shares, which might have been
2674
+            // set by ancient bugs
2675
+            $row['expiration'] = null;
2676
+        }
2677
+    }
2678
+
2679
+    /**
2680
+     * format result
2681
+     * @param array $items result
2682
+     * @param string $column is it a file share or a general share ('file_target' or 'item_target')
2683
+     * @param \OCP\Share_Backend $backend sharing backend
2684
+     * @param int $format
2685
+     * @param array $parameters additional format parameters
2686
+     * @return array format result
2687
+     */
2688
+    private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE , $parameters = null) {
2689
+        if ($format === self::FORMAT_NONE) {
2690
+            return $items;
2691
+        } else if ($format === self::FORMAT_STATUSES) {
2692
+            $statuses = array();
2693
+            foreach ($items as $item) {
2694
+                if ($item['share_type'] === self::SHARE_TYPE_LINK) {
2695
+                    if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) {
2696
+                        continue;
2697
+                    }
2698
+                    $statuses[$item[$column]]['link'] = true;
2699
+                } else if (!isset($statuses[$item[$column]])) {
2700
+                    $statuses[$item[$column]]['link'] = false;
2701
+                }
2702
+                if (!empty($item['file_target'])) {
2703
+                    $statuses[$item[$column]]['path'] = $item['path'];
2704
+                }
2705
+            }
2706
+            return $statuses;
2707
+        } else {
2708
+            return $backend->formatItems($items, $format, $parameters);
2709
+        }
2710
+    }
2711
+
2712
+    /**
2713
+     * remove protocol from URL
2714
+     *
2715
+     * @param string $url
2716
+     * @return string
2717
+     */
2718
+    public static function removeProtocolFromUrl($url) {
2719
+        if (strpos($url, 'https://') === 0) {
2720
+            return substr($url, strlen('https://'));
2721
+        } else if (strpos($url, 'http://') === 0) {
2722
+            return substr($url, strlen('http://'));
2723
+        }
2724
+
2725
+        return $url;
2726
+    }
2727
+
2728
+    /**
2729
+     * try http post first with https and then with http as a fallback
2730
+     *
2731
+     * @param string $remoteDomain
2732
+     * @param string $urlSuffix
2733
+     * @param array $fields post parameters
2734
+     * @return array
2735
+     */
2736
+    private static function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields) {
2737
+        $protocol = 'https://';
2738
+        $result = [
2739
+            'success' => false,
2740
+            'result' => '',
2741
+        ];
2742
+        $try = 0;
2743
+        $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
2744
+        while ($result['success'] === false && $try < 2) {
2745
+            $federationEndpoints = $discoveryService->discover($protocol . $remoteDomain, 'FEDERATED_SHARING');
2746
+            $endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
2747
+            $result = \OC::$server->getHTTPHelper()->post($protocol . $remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, $fields);
2748
+            $try++;
2749
+            $protocol = 'http://';
2750
+        }
2751
+
2752
+        return $result;
2753
+    }
2754
+
2755
+    /**
2756
+     * send server-to-server share to remote server
2757
+     *
2758
+     * @param string $token
2759
+     * @param string $shareWith
2760
+     * @param string $name
2761
+     * @param int $remote_id
2762
+     * @param string $owner
2763
+     * @return bool
2764
+     */
2765
+    private static function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner) {
2766
+
2767
+        list($user, $remote) = Helper::splitUserRemote($shareWith);
2768
+
2769
+        if ($user && $remote) {
2770
+            $url = $remote;
2771
+
2772
+            $local = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
2773
+
2774
+            $fields = array(
2775
+                'shareWith' => $user,
2776
+                'token' => $token,
2777
+                'name' => $name,
2778
+                'remoteId' => $remote_id,
2779
+                'owner' => $owner,
2780
+                'remote' => $local,
2781
+            );
2782
+
2783
+            $url = self::removeProtocolFromUrl($url);
2784
+            $result = self::tryHttpPostToShareEndpoint($url, '', $fields);
2785
+            $status = json_decode($result['result'], true);
2786
+
2787
+            if ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200)) {
2788
+                \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
2789
+                return true;
2790
+            }
2791
+
2792
+        }
2793
+
2794
+        return false;
2795
+    }
2796
+
2797
+    /**
2798
+     * send server-to-server unshare to remote server
2799
+     *
2800
+     * @param string $remote url
2801
+     * @param int $id share id
2802
+     * @param string $token
2803
+     * @return bool
2804
+     */
2805
+    private static function sendRemoteUnshare($remote, $id, $token) {
2806
+        $url = rtrim($remote, '/');
2807
+        $fields = array('token' => $token, 'format' => 'json');
2808
+        $url = self::removeProtocolFromUrl($url);
2809
+        $result = self::tryHttpPostToShareEndpoint($url, '/'.$id.'/unshare', $fields);
2810
+        $status = json_decode($result['result'], true);
2811
+
2812
+        return ($result['success'] && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200));
2813
+    }
2814
+
2815
+    /**
2816
+     * check if user can only share with group members
2817
+     * @return bool
2818
+     */
2819
+    public static function shareWithGroupMembersOnly() {
2820
+        $value = \OC::$server->getAppConfig()->getValue('core', 'shareapi_only_share_with_group_members', 'no');
2821
+        return ($value === 'yes') ? true : false;
2822
+    }
2823
+
2824
+    /**
2825
+     * @return bool
2826
+     */
2827
+    public static function isDefaultExpireDateEnabled() {
2828
+        $defaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
2829
+        return ($defaultExpireDateEnabled === "yes") ? true : false;
2830
+    }
2831
+
2832
+    /**
2833
+     * @return bool
2834
+     */
2835
+    public static function enforceDefaultExpireDate() {
2836
+        $enforceDefaultExpireDate = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
2837
+        return ($enforceDefaultExpireDate === "yes") ? true : false;
2838
+    }
2839
+
2840
+    /**
2841
+     * @return int
2842
+     */
2843
+    public static function getExpireInterval() {
2844
+        return (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
2845
+    }
2846
+
2847
+    /**
2848
+     * Checks whether the given path is reachable for the given owner
2849
+     *
2850
+     * @param string $path path relative to files
2851
+     * @param string $ownerStorageId storage id of the owner
2852
+     *
2853
+     * @return boolean true if file is reachable, false otherwise
2854
+     */
2855
+    private static function isFileReachable($path, $ownerStorageId) {
2856
+        // if outside the home storage, file is always considered reachable
2857
+        if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
2858
+            substr($ownerStorageId, 0, 13) === 'object::user:'
2859
+        )) {
2860
+            return true;
2861
+        }
2862
+
2863
+        // if inside the home storage, the file has to be under "/files/"
2864
+        $path = ltrim($path, '/');
2865
+        if (substr($path, 0, 6) === 'files/') {
2866
+            return true;
2867
+        }
2868
+
2869
+        return false;
2870
+    }
2871
+
2872
+    /**
2873
+     * @param IConfig $config
2874
+     * @return bool
2875
+     */
2876
+    public static function enforcePassword(IConfig $config) {
2877
+        $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
2878
+        return ($enforcePassword === "yes") ? true : false;
2879
+    }
2880
+
2881
+    /**
2882
+     * Get all share entries, including non-unique group items
2883
+     *
2884
+     * @param string $owner
2885
+     * @return array
2886
+     */
2887
+    public static function getAllSharesForOwner($owner) {
2888
+        $query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?';
2889
+        $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]);
2890
+        return $result->fetchAll();
2891
+    }
2892
+
2893
+    /**
2894
+     * Get all share entries, including non-unique group items for a file
2895
+     *
2896
+     * @param int $id
2897
+     * @return array
2898
+     */
2899
+    public static function getAllSharesForFileId($id) {
2900
+        $query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?';
2901
+        $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]);
2902
+        return $result->fetchAll();
2903
+    }
2904
+
2905
+    /**
2906
+     * @param string $password
2907
+     * @throws \Exception
2908
+     */
2909
+    private static function verifyPassword($password) {
2910
+
2911
+        $accepted = true;
2912
+        $message = '';
2913
+        \OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
2914
+            'password' => $password,
2915
+            'accepted' => &$accepted,
2916
+            'message' => &$message
2917
+        ]);
2918
+
2919
+        if (!$accepted) {
2920
+            throw new \Exception($message);
2921
+        }
2922
+    }
2923 2923
 }
Please login to merge, or discard this patch.
apps/sharebymail/lib/ShareByMailProvider.php 2 patches
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -209,10 +209,10 @@  discard block
 block discarded – undo
209 209
 		}
210 210
 
211 211
 		$passwordPolicy = $this->getPasswordPolicy();
212
-		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
212
+		$passwordCharset = ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS;
213 213
 		$passwordLength = 8;
214 214
 		if (!empty($passwordPolicy)) {
215
-			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
215
+			$passwordLength = (int) $passwordPolicy['minLength'] > 0 ? (int) $passwordPolicy['minLength'] : $passwordLength;
216 216
 			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
217 217
 		}
218 218
 
@@ -349,11 +349,11 @@  discard block
 block discarded – undo
349 349
 				$share->getSharedWith()
350 350
 			);
351 351
 		} catch (HintException $hintException) {
352
-			$this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
352
+			$this->logger->error('Failed to send share by mail: '.$hintException->getMessage());
353 353
 			$this->removeShareFromTable($shareId);
354 354
 			throw $hintException;
355 355
 		} catch (\Exception $e) {
356
-			$this->logger->error('Failed to send share by email: ' . $e->getMessage());
356
+			$this->logger->error('Failed to send share by email: '.$e->getMessage());
357 357
 			$this->removeShareFromTable($shareId);
358 358
 			throw new HintException('Failed to send share by mail',
359 359
 				$this->l->t('Failed to send share by email'));
@@ -376,7 +376,7 @@  discard block
 block discarded – undo
376 376
 											$shareWith) {
377 377
 		$initiatorUser = $this->userManager->get($initiator);
378 378
 		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
379
-		$subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
379
+		$subject = (string) $this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
380 380
 
381 381
 		$message = $this->mailer->createMessage();
382 382
 
@@ -387,7 +387,7 @@  discard block
 block discarded – undo
387 387
 		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
388 388
 
389 389
 		$emailTemplate->addBodyText(
390
-			$text . ' ' . $this->l->t('Click the button below to open it.'),
390
+			$text.' '.$this->l->t('Click the button below to open it.'),
391 391
 			$text
392 392
 		);
393 393
 		$emailTemplate->addBodyButton(
@@ -411,9 +411,9 @@  discard block
 block discarded – undo
411 411
 		// The "Reply-To" is set to the sharer if an mail address is configured
412 412
 		// also the default footer contains a "Do not reply" which needs to be adjusted.
413 413
 		$initiatorEmail = $initiatorUser->getEMailAddress();
414
-		if($initiatorEmail !== null) {
414
+		if ($initiatorEmail !== null) {
415 415
 			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
416
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
416
+			$emailTemplate->addFooter($instanceName.' - '.$this->defaults->getSlogan());
417 417
 		} else {
418 418
 			$emailTemplate->addFooter();
419 419
 		}
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
 		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
446 446
 		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
447 447
 
448
-		$subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
448
+		$subject = (string) $this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
449 449
 		$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
450 450
 		$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
451 451
 
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
 		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
470 470
 		if ($initiatorEmailAddress !== null) {
471 471
 			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
472
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
472
+			$emailTemplate->addFooter($instanceName.' - '.$this->defaults->getSlogan());
473 473
 		} else {
474 474
 			$emailTemplate->addFooter();
475 475
 		}
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
 			);
509 509
 		}
510 510
 
511
-		$subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
511
+		$subject = (string) $this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
512 512
 		$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %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()]);
513 513
 
514 514
 		$message = $this->mailer->createMessage();
@@ -562,7 +562,7 @@  discard block
 block discarded – undo
562 562
 			->orderBy('id');
563 563
 
564 564
 		$cursor = $qb->execute();
565
-		while($data = $cursor->fetch()) {
565
+		while ($data = $cursor->fetch()) {
566 566
 			$children[] = $this->createShareObject($data);
567 567
 		}
568 568
 		$cursor->closeCursor();
@@ -606,7 +606,7 @@  discard block
 block discarded – undo
606 606
 		$qb->execute();
607 607
 		$id = $qb->getLastInsertId();
608 608
 
609
-		return (int)$id;
609
+		return (int) $id;
610 610
 	}
611 611
 
612 612
 	/**
@@ -623,7 +623,7 @@  discard block
 block discarded – undo
623 623
 		// a real password was given
624 624
 		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
625 625
 
626
-		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
626
+		if ($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
627 627
 			$this->sendPassword($share, $plainTextPassword);
628 628
 		}
629 629
 		/*
@@ -717,7 +717,7 @@  discard block
 block discarded – undo
717 717
 
718 718
 		$cursor = $qb->execute();
719 719
 		$shares = [];
720
-		while($data = $cursor->fetch()) {
720
+		while ($data = $cursor->fetch()) {
721 721
 			$shares[] = $this->createShareObject($data);
722 722
 		}
723 723
 		$cursor->closeCursor();
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
 			->execute();
770 770
 
771 771
 		$shares = [];
772
-		while($data = $cursor->fetch()) {
772
+		while ($data = $cursor->fetch()) {
773 773
 			$shares[] = $this->createShareObject($data);
774 774
 		}
775 775
 		$cursor->closeCursor();
@@ -808,7 +808,7 @@  discard block
 block discarded – undo
808 808
 
809 809
 		$cursor = $qb->execute();
810 810
 
811
-		while($data = $cursor->fetch()) {
811
+		while ($data = $cursor->fetch()) {
812 812
 			$shares[] = $this->createShareObject($data);
813 813
 		}
814 814
 		$cursor->closeCursor();
@@ -871,15 +871,15 @@  discard block
 block discarded – undo
871 871
 	protected function createShareObject($data) {
872 872
 
873 873
 		$share = new Share($this->rootFolder, $this->userManager);
874
-		$share->setId((int)$data['id'])
875
-			->setShareType((int)$data['share_type'])
876
-			->setPermissions((int)$data['permissions'])
874
+		$share->setId((int) $data['id'])
875
+			->setShareType((int) $data['share_type'])
876
+			->setPermissions((int) $data['permissions'])
877 877
 			->setTarget($data['file_target'])
878
-			->setMailSend((bool)$data['mail_send'])
878
+			->setMailSend((bool) $data['mail_send'])
879 879
 			->setToken($data['token']);
880 880
 
881 881
 		$shareTime = new \DateTime();
882
-		$shareTime->setTimestamp((int)$data['stime']);
882
+		$shareTime->setTimestamp((int) $data['stime']);
883 883
 		$share->setShareTime($shareTime);
884 884
 		$share->setSharedWith($data['share_with']);
885 885
 		$share->setPassword($data['password']);
@@ -890,7 +890,7 @@  discard block
 block discarded – undo
890 890
 		} else {
891 891
 			//OLD SHARE
892 892
 			$share->setSharedBy($data['uid_owner']);
893
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
893
+			$path = $this->getNode($share->getSharedBy(), (int) $data['file_source']);
894 894
 
895 895
 			$owner = $path->getOwner();
896 896
 			$share->setShareOwner($owner->getUID());
@@ -903,7 +903,7 @@  discard block
 block discarded – undo
903 903
 			}
904 904
 		}
905 905
 
906
-		$share->setNodeId((int)$data['file_source']);
906
+		$share->setNodeId((int) $data['file_source']);
907 907
 		$share->setNodeType($data['item_type']);
908 908
 
909 909
 		$share->setProviderId($this->identifier());
@@ -1022,7 +1022,7 @@  discard block
 block discarded – undo
1022 1022
 			);
1023 1023
 		}
1024 1024
 
1025
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1025
+		$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1026 1026
 		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1027 1027
 
1028 1028
 		$qb->orderBy('id');
Please login to merge, or discard this patch.
Indentation   +998 added lines, -998 removed lines patch added patch discarded remove patch
@@ -53,1016 +53,1016 @@
 block discarded – undo
53 53
  */
54 54
 class ShareByMailProvider implements IShareProvider {
55 55
 
56
-	/** @var  IDBConnection */
57
-	private $dbConnection;
58
-
59
-	/** @var ILogger */
60
-	private $logger;
61
-
62
-	/** @var ISecureRandom */
63
-	private $secureRandom;
64
-
65
-	/** @var IUserManager */
66
-	private $userManager;
67
-
68
-	/** @var IRootFolder */
69
-	private $rootFolder;
70
-
71
-	/** @var IL10N */
72
-	private $l;
73
-
74
-	/** @var IMailer */
75
-	private $mailer;
76
-
77
-	/** @var IURLGenerator */
78
-	private $urlGenerator;
79
-
80
-	/** @var IManager  */
81
-	private $activityManager;
82
-
83
-	/** @var SettingsManager */
84
-	private $settingsManager;
85
-
86
-	/** @var Defaults */
87
-	private $defaults;
88
-
89
-	/** @var IHasher */
90
-	private $hasher;
91
-
92
-	/** @var  CapabilitiesManager */
93
-	private $capabilitiesManager;
94
-
95
-	/**
96
-	 * Return the identifier of this provider.
97
-	 *
98
-	 * @return string Containing only [a-zA-Z0-9]
99
-	 */
100
-	public function identifier() {
101
-		return 'ocMailShare';
102
-	}
103
-
104
-	/**
105
-	 * DefaultShareProvider constructor.
106
-	 *
107
-	 * @param IDBConnection $connection
108
-	 * @param ISecureRandom $secureRandom
109
-	 * @param IUserManager $userManager
110
-	 * @param IRootFolder $rootFolder
111
-	 * @param IL10N $l
112
-	 * @param ILogger $logger
113
-	 * @param IMailer $mailer
114
-	 * @param IURLGenerator $urlGenerator
115
-	 * @param IManager $activityManager
116
-	 * @param SettingsManager $settingsManager
117
-	 * @param Defaults $defaults
118
-	 * @param IHasher $hasher
119
-	 * @param CapabilitiesManager $capabilitiesManager
120
-	 */
121
-	public function __construct(
122
-		IDBConnection $connection,
123
-		ISecureRandom $secureRandom,
124
-		IUserManager $userManager,
125
-		IRootFolder $rootFolder,
126
-		IL10N $l,
127
-		ILogger $logger,
128
-		IMailer $mailer,
129
-		IURLGenerator $urlGenerator,
130
-		IManager $activityManager,
131
-		SettingsManager $settingsManager,
132
-		Defaults $defaults,
133
-		IHasher $hasher,
134
-		CapabilitiesManager $capabilitiesManager
135
-	) {
136
-		$this->dbConnection = $connection;
137
-		$this->secureRandom = $secureRandom;
138
-		$this->userManager = $userManager;
139
-		$this->rootFolder = $rootFolder;
140
-		$this->l = $l;
141
-		$this->logger = $logger;
142
-		$this->mailer = $mailer;
143
-		$this->urlGenerator = $urlGenerator;
144
-		$this->activityManager = $activityManager;
145
-		$this->settingsManager = $settingsManager;
146
-		$this->defaults = $defaults;
147
-		$this->hasher = $hasher;
148
-		$this->capabilitiesManager = $capabilitiesManager;
149
-	}
150
-
151
-	/**
152
-	 * Share a path
153
-	 *
154
-	 * @param IShare $share
155
-	 * @return IShare The share object
156
-	 * @throws ShareNotFound
157
-	 * @throws \Exception
158
-	 */
159
-	public function create(IShare $share) {
160
-
161
-		$shareWith = $share->getSharedWith();
162
-		/*
56
+    /** @var  IDBConnection */
57
+    private $dbConnection;
58
+
59
+    /** @var ILogger */
60
+    private $logger;
61
+
62
+    /** @var ISecureRandom */
63
+    private $secureRandom;
64
+
65
+    /** @var IUserManager */
66
+    private $userManager;
67
+
68
+    /** @var IRootFolder */
69
+    private $rootFolder;
70
+
71
+    /** @var IL10N */
72
+    private $l;
73
+
74
+    /** @var IMailer */
75
+    private $mailer;
76
+
77
+    /** @var IURLGenerator */
78
+    private $urlGenerator;
79
+
80
+    /** @var IManager  */
81
+    private $activityManager;
82
+
83
+    /** @var SettingsManager */
84
+    private $settingsManager;
85
+
86
+    /** @var Defaults */
87
+    private $defaults;
88
+
89
+    /** @var IHasher */
90
+    private $hasher;
91
+
92
+    /** @var  CapabilitiesManager */
93
+    private $capabilitiesManager;
94
+
95
+    /**
96
+     * Return the identifier of this provider.
97
+     *
98
+     * @return string Containing only [a-zA-Z0-9]
99
+     */
100
+    public function identifier() {
101
+        return 'ocMailShare';
102
+    }
103
+
104
+    /**
105
+     * DefaultShareProvider constructor.
106
+     *
107
+     * @param IDBConnection $connection
108
+     * @param ISecureRandom $secureRandom
109
+     * @param IUserManager $userManager
110
+     * @param IRootFolder $rootFolder
111
+     * @param IL10N $l
112
+     * @param ILogger $logger
113
+     * @param IMailer $mailer
114
+     * @param IURLGenerator $urlGenerator
115
+     * @param IManager $activityManager
116
+     * @param SettingsManager $settingsManager
117
+     * @param Defaults $defaults
118
+     * @param IHasher $hasher
119
+     * @param CapabilitiesManager $capabilitiesManager
120
+     */
121
+    public function __construct(
122
+        IDBConnection $connection,
123
+        ISecureRandom $secureRandom,
124
+        IUserManager $userManager,
125
+        IRootFolder $rootFolder,
126
+        IL10N $l,
127
+        ILogger $logger,
128
+        IMailer $mailer,
129
+        IURLGenerator $urlGenerator,
130
+        IManager $activityManager,
131
+        SettingsManager $settingsManager,
132
+        Defaults $defaults,
133
+        IHasher $hasher,
134
+        CapabilitiesManager $capabilitiesManager
135
+    ) {
136
+        $this->dbConnection = $connection;
137
+        $this->secureRandom = $secureRandom;
138
+        $this->userManager = $userManager;
139
+        $this->rootFolder = $rootFolder;
140
+        $this->l = $l;
141
+        $this->logger = $logger;
142
+        $this->mailer = $mailer;
143
+        $this->urlGenerator = $urlGenerator;
144
+        $this->activityManager = $activityManager;
145
+        $this->settingsManager = $settingsManager;
146
+        $this->defaults = $defaults;
147
+        $this->hasher = $hasher;
148
+        $this->capabilitiesManager = $capabilitiesManager;
149
+    }
150
+
151
+    /**
152
+     * Share a path
153
+     *
154
+     * @param IShare $share
155
+     * @return IShare The share object
156
+     * @throws ShareNotFound
157
+     * @throws \Exception
158
+     */
159
+    public function create(IShare $share) {
160
+
161
+        $shareWith = $share->getSharedWith();
162
+        /*
163 163
 		 * Check if file is not already shared with the remote user
164 164
 		 */
165
-		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
-		if (!empty($alreadyShared)) {
167
-			$message = 'Sharing %s failed, this item is already shared with %s';
168
-			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
-			throw new \Exception($message_t);
171
-		}
172
-
173
-		// if the admin enforces a password for all mail shares we create a
174
-		// random password and send it to the recipient
175
-		$password = '';
176
-		$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
-		if ($passwordEnforced) {
178
-			$password = $this->autoGeneratePassword($share);
179
-		}
180
-
181
-		$shareId = $this->createMailShare($share);
182
-		$send = $this->sendPassword($share, $password);
183
-		if ($passwordEnforced && $send === false) {
184
-			$this->sendPasswordToOwner($share, $password);
185
-		}
186
-
187
-		$this->createShareActivity($share);
188
-		$data = $this->getRawShare($shareId);
189
-
190
-		return $this->createShareObject($data);
191
-
192
-	}
193
-
194
-	/**
195
-	 * auto generate password in case of password enforcement on mail shares
196
-	 *
197
-	 * @param IShare $share
198
-	 * @return string
199
-	 * @throws \Exception
200
-	 */
201
-	protected function autoGeneratePassword($share) {
202
-		$initiatorUser = $this->userManager->get($share->getSharedBy());
203
-		$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
-		$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
-
206
-		if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
-			throw new \Exception(
208
-				$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.")
209
-			);
210
-		}
211
-
212
-		$passwordPolicy = $this->getPasswordPolicy();
213
-		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
-		$passwordLength = 8;
215
-		if (!empty($passwordPolicy)) {
216
-			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
-			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
-		}
219
-
220
-		$password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
-
222
-		$share->setPassword($this->hasher->hash($password));
223
-
224
-		return $password;
225
-	}
226
-
227
-	/**
228
-	 * get password policy
229
-	 *
230
-	 * @return array
231
-	 */
232
-	protected function getPasswordPolicy() {
233
-		$capabilities = $this->capabilitiesManager->getCapabilities();
234
-		if (isset($capabilities['password_policy'])) {
235
-			return $capabilities['password_policy'];
236
-		}
237
-
238
-		return [];
239
-	}
240
-
241
-	/**
242
-	 * create activity if a file/folder was shared by mail
243
-	 *
244
-	 * @param IShare $share
245
-	 */
246
-	protected function createShareActivity(IShare $share) {
247
-
248
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
-
250
-		$this->publishActivity(
251
-			Activity::SUBJECT_SHARED_EMAIL_SELF,
252
-			[$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
-			$share->getSharedBy(),
254
-			$share->getNode()->getId(),
255
-			$userFolder->getRelativePath($share->getNode()->getPath())
256
-		);
257
-
258
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
259
-			$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
-			$fileId = $share->getNode()->getId();
261
-			$nodes = $ownerFolder->getById($fileId);
262
-			$ownerPath = $nodes[0]->getPath();
263
-			$this->publishActivity(
264
-				Activity::SUBJECT_SHARED_EMAIL_BY,
265
-				[$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
-				$share->getShareOwner(),
267
-				$fileId,
268
-				$ownerFolder->getRelativePath($ownerPath)
269
-			);
270
-		}
271
-
272
-	}
273
-
274
-	/**
275
-	 * create activity if a file/folder was shared by mail
276
-	 *
277
-	 * @param IShare $share
278
-	 * @param string $sharedWith
279
-	 * @param bool $sendToSelf
280
-	 */
281
-	protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
-
283
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
-
285
-		if ($sendToSelf) {
286
-			$this->publishActivity(
287
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
-				[$userFolder->getRelativePath($share->getNode()->getPath())],
289
-				$share->getSharedBy(),
290
-				$share->getNode()->getId(),
291
-				$userFolder->getRelativePath($share->getNode()->getPath())
292
-			);
293
-		} else {
294
-			$this->publishActivity(
295
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
-				[$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
-				$share->getSharedBy(),
298
-				$share->getNode()->getId(),
299
-				$userFolder->getRelativePath($share->getNode()->getPath())
300
-			);
301
-		}
302
-	}
303
-
304
-
305
-	/**
306
-	 * publish activity if a file/folder was shared by mail
307
-	 *
308
-	 * @param $subject
309
-	 * @param $parameters
310
-	 * @param $affectedUser
311
-	 * @param $fileId
312
-	 * @param $filePath
313
-	 */
314
-	protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
-		$event = $this->activityManager->generateEvent();
316
-		$event->setApp('sharebymail')
317
-			->setType('shared')
318
-			->setSubject($subject, $parameters)
319
-			->setAffectedUser($affectedUser)
320
-			->setObject('files', $fileId, $filePath);
321
-		$this->activityManager->publish($event);
322
-
323
-	}
324
-
325
-	/**
326
-	 * @param IShare $share
327
-	 * @return int
328
-	 * @throws \Exception
329
-	 */
330
-	protected function createMailShare(IShare $share) {
331
-		$share->setToken($this->generateToken());
332
-		$shareId = $this->addShareToDB(
333
-			$share->getNodeId(),
334
-			$share->getNodeType(),
335
-			$share->getSharedWith(),
336
-			$share->getSharedBy(),
337
-			$share->getShareOwner(),
338
-			$share->getPermissions(),
339
-			$share->getToken(),
340
-			$share->getPassword()
341
-		);
342
-
343
-		try {
344
-			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
-				['token' => $share->getToken()]);
346
-			$this->sendMailNotification(
347
-				$share->getNode()->getName(),
348
-				$link,
349
-				$share->getSharedBy(),
350
-				$share->getSharedWith()
351
-			);
352
-		} catch (HintException $hintException) {
353
-			$this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
354
-			$this->removeShareFromTable($shareId);
355
-			throw $hintException;
356
-		} catch (\Exception $e) {
357
-			$this->logger->error('Failed to send share by email: ' . $e->getMessage());
358
-			$this->removeShareFromTable($shareId);
359
-			throw new HintException('Failed to send share by mail',
360
-				$this->l->t('Failed to send share by email'));
361
-		}
362
-
363
-		return $shareId;
364
-
365
-	}
366
-
367
-	/**
368
-	 * @param string $filename
369
-	 * @param string $link
370
-	 * @param string $initiator
371
-	 * @param string $shareWith
372
-	 * @throws \Exception If mail couldn't be sent
373
-	 */
374
-	protected function sendMailNotification($filename,
375
-											$link,
376
-											$initiator,
377
-											$shareWith) {
378
-		$initiatorUser = $this->userManager->get($initiator);
379
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
380
-		$subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
381
-
382
-		$message = $this->mailer->createMessage();
383
-
384
-		$emailTemplate = $this->mailer->createEMailTemplate();
385
-
386
-		$emailTemplate->addHeader();
387
-		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
388
-		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
389
-
390
-		$emailTemplate->addBodyText(
391
-			$text . ' ' . $this->l->t('Click the button below to open it.'),
392
-			$text
393
-		);
394
-		$emailTemplate->addBodyButton(
395
-			$this->l->t('Open »%s«', [$filename]),
396
-			$link
397
-		);
398
-
399
-		$message->setTo([$shareWith]);
400
-
401
-		// The "From" contains the sharers name
402
-		$instanceName = $this->defaults->getName();
403
-		$senderName = $this->l->t(
404
-			'%s via %s',
405
-			[
406
-				$initiatorDisplayName,
407
-				$instanceName
408
-			]
409
-		);
410
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
411
-
412
-		// The "Reply-To" is set to the sharer if an mail address is configured
413
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
414
-		$initiatorEmail = $initiatorUser->getEMailAddress();
415
-		if($initiatorEmail !== null) {
416
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
417
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
418
-		} else {
419
-			$emailTemplate->addFooter();
420
-		}
421
-
422
-		$message->setSubject($subject);
423
-		$message->setPlainBody($emailTemplate->renderText());
424
-		$message->setHtmlBody($emailTemplate->renderHtml());
425
-		$this->mailer->send($message);
426
-	}
427
-
428
-	/**
429
-	 * send password to recipient of a mail share
430
-	 *
431
-	 * @param IShare $share
432
-	 * @param string $password
433
-	 * @return bool
434
-	 */
435
-	protected function sendPassword(IShare $share, $password) {
436
-
437
-		$filename = $share->getNode()->getName();
438
-		$initiator = $share->getSharedBy();
439
-		$shareWith = $share->getSharedWith();
440
-
441
-		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
442
-			return false;
443
-		}
444
-
445
-		$initiatorUser = $this->userManager->get($initiator);
446
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
447
-		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
448
-
449
-		$subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
450
-		$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
451
-		$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
452
-
453
-		$message = $this->mailer->createMessage();
454
-
455
-		$emailTemplate = $this->mailer->createEMailTemplate();
456
-		$emailTemplate->addHeader();
457
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
458
-		$emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
459
-		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
460
-
461
-		// The "From" contains the sharers name
462
-		$instanceName = $this->defaults->getName();
463
-		$senderName = $this->l->t(
464
-			'%s via %s',
465
-			[
466
-				$initiatorDisplayName,
467
-				$instanceName
468
-			]
469
-		);
470
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
471
-		if ($initiatorEmailAddress !== null) {
472
-			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
473
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
474
-		} else {
475
-			$emailTemplate->addFooter();
476
-		}
477
-
478
-		$message->setTo([$shareWith]);
479
-		$message->setSubject($subject);
480
-		$message->setBody($emailTemplate->renderText(), 'text/plain');
481
-		$message->setHtmlBody($emailTemplate->renderHtml());
482
-		$this->mailer->send($message);
483
-
484
-		$this->createPasswordSendActivity($share, $shareWith, false);
485
-
486
-		return true;
487
-	}
488
-
489
-	/**
490
-	 * send auto generated password to the owner. This happens if the admin enforces
491
-	 * a password for mail shares and forbid to send the password by mail to the recipient
492
-	 *
493
-	 * @param IShare $share
494
-	 * @param string $password
495
-	 * @return bool
496
-	 * @throws \Exception
497
-	 */
498
-	protected function sendPasswordToOwner(IShare $share, $password) {
499
-
500
-		$filename = $share->getNode()->getName();
501
-		$initiator = $this->userManager->get($share->getSharedBy());
502
-		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
503
-		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
504
-		$shareWith = $share->getSharedWith();
505
-
506
-		if ($initiatorEMailAddress === null) {
507
-			throw new \Exception(
508
-				$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.")
509
-			);
510
-		}
511
-
512
-		$subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
513
-		$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %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()]);
514
-
515
-		$message = $this->mailer->createMessage();
516
-		$emailTemplate = $this->mailer->createEMailTemplate();
517
-
518
-		$emailTemplate->addHeader();
519
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
520
-		$emailTemplate->addBodyText($bodyPart);
521
-		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
522
-		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
523
-		$emailTemplate->addFooter();
524
-
525
-		if ($initiatorEMailAddress) {
526
-			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
527
-		}
528
-		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
529
-		$message->setSubject($subject);
530
-		$message->setBody($emailTemplate->renderText(), 'text/plain');
531
-		$message->setHtmlBody($emailTemplate->renderHtml());
532
-		$this->mailer->send($message);
533
-
534
-		$this->createPasswordSendActivity($share, $shareWith, true);
535
-
536
-		return true;
537
-	}
538
-
539
-	/**
540
-	 * generate share token
541
-	 *
542
-	 * @return string
543
-	 */
544
-	protected function generateToken($size = 15) {
545
-		$token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
546
-		return $token;
547
-	}
548
-
549
-	/**
550
-	 * Get all children of this share
551
-	 *
552
-	 * @param IShare $parent
553
-	 * @return IShare[]
554
-	 */
555
-	public function getChildren(IShare $parent) {
556
-		$children = [];
557
-
558
-		$qb = $this->dbConnection->getQueryBuilder();
559
-		$qb->select('*')
560
-			->from('share')
561
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
562
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
563
-			->orderBy('id');
564
-
565
-		$cursor = $qb->execute();
566
-		while($data = $cursor->fetch()) {
567
-			$children[] = $this->createShareObject($data);
568
-		}
569
-		$cursor->closeCursor();
570
-
571
-		return $children;
572
-	}
573
-
574
-	/**
575
-	 * add share to the database and return the ID
576
-	 *
577
-	 * @param int $itemSource
578
-	 * @param string $itemType
579
-	 * @param string $shareWith
580
-	 * @param string $sharedBy
581
-	 * @param string $uidOwner
582
-	 * @param int $permissions
583
-	 * @param string $token
584
-	 * @return int
585
-	 */
586
-	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
587
-		$qb = $this->dbConnection->getQueryBuilder();
588
-		$qb->insert('share')
589
-			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
590
-			->setValue('item_type', $qb->createNamedParameter($itemType))
591
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
592
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
593
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
594
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
595
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
596
-			->setValue('permissions', $qb->createNamedParameter($permissions))
597
-			->setValue('token', $qb->createNamedParameter($token))
598
-			->setValue('password', $qb->createNamedParameter($password))
599
-			->setValue('stime', $qb->createNamedParameter(time()));
600
-
601
-		/*
165
+        $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
+        if (!empty($alreadyShared)) {
167
+            $message = 'Sharing %s failed, this item is already shared with %s';
168
+            $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
+            throw new \Exception($message_t);
171
+        }
172
+
173
+        // if the admin enforces a password for all mail shares we create a
174
+        // random password and send it to the recipient
175
+        $password = '';
176
+        $passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
+        if ($passwordEnforced) {
178
+            $password = $this->autoGeneratePassword($share);
179
+        }
180
+
181
+        $shareId = $this->createMailShare($share);
182
+        $send = $this->sendPassword($share, $password);
183
+        if ($passwordEnforced && $send === false) {
184
+            $this->sendPasswordToOwner($share, $password);
185
+        }
186
+
187
+        $this->createShareActivity($share);
188
+        $data = $this->getRawShare($shareId);
189
+
190
+        return $this->createShareObject($data);
191
+
192
+    }
193
+
194
+    /**
195
+     * auto generate password in case of password enforcement on mail shares
196
+     *
197
+     * @param IShare $share
198
+     * @return string
199
+     * @throws \Exception
200
+     */
201
+    protected function autoGeneratePassword($share) {
202
+        $initiatorUser = $this->userManager->get($share->getSharedBy());
203
+        $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
+        $allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
+
206
+        if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
+            throw new \Exception(
208
+                $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.")
209
+            );
210
+        }
211
+
212
+        $passwordPolicy = $this->getPasswordPolicy();
213
+        $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
+        $passwordLength = 8;
215
+        if (!empty($passwordPolicy)) {
216
+            $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
+            $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
+        }
219
+
220
+        $password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
+
222
+        $share->setPassword($this->hasher->hash($password));
223
+
224
+        return $password;
225
+    }
226
+
227
+    /**
228
+     * get password policy
229
+     *
230
+     * @return array
231
+     */
232
+    protected function getPasswordPolicy() {
233
+        $capabilities = $this->capabilitiesManager->getCapabilities();
234
+        if (isset($capabilities['password_policy'])) {
235
+            return $capabilities['password_policy'];
236
+        }
237
+
238
+        return [];
239
+    }
240
+
241
+    /**
242
+     * create activity if a file/folder was shared by mail
243
+     *
244
+     * @param IShare $share
245
+     */
246
+    protected function createShareActivity(IShare $share) {
247
+
248
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
+
250
+        $this->publishActivity(
251
+            Activity::SUBJECT_SHARED_EMAIL_SELF,
252
+            [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
+            $share->getSharedBy(),
254
+            $share->getNode()->getId(),
255
+            $userFolder->getRelativePath($share->getNode()->getPath())
256
+        );
257
+
258
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
259
+            $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
+            $fileId = $share->getNode()->getId();
261
+            $nodes = $ownerFolder->getById($fileId);
262
+            $ownerPath = $nodes[0]->getPath();
263
+            $this->publishActivity(
264
+                Activity::SUBJECT_SHARED_EMAIL_BY,
265
+                [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
+                $share->getShareOwner(),
267
+                $fileId,
268
+                $ownerFolder->getRelativePath($ownerPath)
269
+            );
270
+        }
271
+
272
+    }
273
+
274
+    /**
275
+     * create activity if a file/folder was shared by mail
276
+     *
277
+     * @param IShare $share
278
+     * @param string $sharedWith
279
+     * @param bool $sendToSelf
280
+     */
281
+    protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
+
283
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
+
285
+        if ($sendToSelf) {
286
+            $this->publishActivity(
287
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
+                [$userFolder->getRelativePath($share->getNode()->getPath())],
289
+                $share->getSharedBy(),
290
+                $share->getNode()->getId(),
291
+                $userFolder->getRelativePath($share->getNode()->getPath())
292
+            );
293
+        } else {
294
+            $this->publishActivity(
295
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
+                [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
+                $share->getSharedBy(),
298
+                $share->getNode()->getId(),
299
+                $userFolder->getRelativePath($share->getNode()->getPath())
300
+            );
301
+        }
302
+    }
303
+
304
+
305
+    /**
306
+     * publish activity if a file/folder was shared by mail
307
+     *
308
+     * @param $subject
309
+     * @param $parameters
310
+     * @param $affectedUser
311
+     * @param $fileId
312
+     * @param $filePath
313
+     */
314
+    protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
+        $event = $this->activityManager->generateEvent();
316
+        $event->setApp('sharebymail')
317
+            ->setType('shared')
318
+            ->setSubject($subject, $parameters)
319
+            ->setAffectedUser($affectedUser)
320
+            ->setObject('files', $fileId, $filePath);
321
+        $this->activityManager->publish($event);
322
+
323
+    }
324
+
325
+    /**
326
+     * @param IShare $share
327
+     * @return int
328
+     * @throws \Exception
329
+     */
330
+    protected function createMailShare(IShare $share) {
331
+        $share->setToken($this->generateToken());
332
+        $shareId = $this->addShareToDB(
333
+            $share->getNodeId(),
334
+            $share->getNodeType(),
335
+            $share->getSharedWith(),
336
+            $share->getSharedBy(),
337
+            $share->getShareOwner(),
338
+            $share->getPermissions(),
339
+            $share->getToken(),
340
+            $share->getPassword()
341
+        );
342
+
343
+        try {
344
+            $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
+                ['token' => $share->getToken()]);
346
+            $this->sendMailNotification(
347
+                $share->getNode()->getName(),
348
+                $link,
349
+                $share->getSharedBy(),
350
+                $share->getSharedWith()
351
+            );
352
+        } catch (HintException $hintException) {
353
+            $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage());
354
+            $this->removeShareFromTable($shareId);
355
+            throw $hintException;
356
+        } catch (\Exception $e) {
357
+            $this->logger->error('Failed to send share by email: ' . $e->getMessage());
358
+            $this->removeShareFromTable($shareId);
359
+            throw new HintException('Failed to send share by mail',
360
+                $this->l->t('Failed to send share by email'));
361
+        }
362
+
363
+        return $shareId;
364
+
365
+    }
366
+
367
+    /**
368
+     * @param string $filename
369
+     * @param string $link
370
+     * @param string $initiator
371
+     * @param string $shareWith
372
+     * @throws \Exception If mail couldn't be sent
373
+     */
374
+    protected function sendMailNotification($filename,
375
+                                            $link,
376
+                                            $initiator,
377
+                                            $shareWith) {
378
+        $initiatorUser = $this->userManager->get($initiator);
379
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
380
+        $subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename));
381
+
382
+        $message = $this->mailer->createMessage();
383
+
384
+        $emailTemplate = $this->mailer->createEMailTemplate();
385
+
386
+        $emailTemplate->addHeader();
387
+        $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
388
+        $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
389
+
390
+        $emailTemplate->addBodyText(
391
+            $text . ' ' . $this->l->t('Click the button below to open it.'),
392
+            $text
393
+        );
394
+        $emailTemplate->addBodyButton(
395
+            $this->l->t('Open »%s«', [$filename]),
396
+            $link
397
+        );
398
+
399
+        $message->setTo([$shareWith]);
400
+
401
+        // The "From" contains the sharers name
402
+        $instanceName = $this->defaults->getName();
403
+        $senderName = $this->l->t(
404
+            '%s via %s',
405
+            [
406
+                $initiatorDisplayName,
407
+                $instanceName
408
+            ]
409
+        );
410
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
411
+
412
+        // The "Reply-To" is set to the sharer if an mail address is configured
413
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
414
+        $initiatorEmail = $initiatorUser->getEMailAddress();
415
+        if($initiatorEmail !== null) {
416
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
417
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
418
+        } else {
419
+            $emailTemplate->addFooter();
420
+        }
421
+
422
+        $message->setSubject($subject);
423
+        $message->setPlainBody($emailTemplate->renderText());
424
+        $message->setHtmlBody($emailTemplate->renderHtml());
425
+        $this->mailer->send($message);
426
+    }
427
+
428
+    /**
429
+     * send password to recipient of a mail share
430
+     *
431
+     * @param IShare $share
432
+     * @param string $password
433
+     * @return bool
434
+     */
435
+    protected function sendPassword(IShare $share, $password) {
436
+
437
+        $filename = $share->getNode()->getName();
438
+        $initiator = $share->getSharedBy();
439
+        $shareWith = $share->getSharedWith();
440
+
441
+        if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
442
+            return false;
443
+        }
444
+
445
+        $initiatorUser = $this->userManager->get($initiator);
446
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
447
+        $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
448
+
449
+        $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]);
450
+        $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
451
+        $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
452
+
453
+        $message = $this->mailer->createMessage();
454
+
455
+        $emailTemplate = $this->mailer->createEMailTemplate();
456
+        $emailTemplate->addHeader();
457
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
458
+        $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart);
459
+        $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
460
+
461
+        // The "From" contains the sharers name
462
+        $instanceName = $this->defaults->getName();
463
+        $senderName = $this->l->t(
464
+            '%s via %s',
465
+            [
466
+                $initiatorDisplayName,
467
+                $instanceName
468
+            ]
469
+        );
470
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
471
+        if ($initiatorEmailAddress !== null) {
472
+            $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
473
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
474
+        } else {
475
+            $emailTemplate->addFooter();
476
+        }
477
+
478
+        $message->setTo([$shareWith]);
479
+        $message->setSubject($subject);
480
+        $message->setBody($emailTemplate->renderText(), 'text/plain');
481
+        $message->setHtmlBody($emailTemplate->renderHtml());
482
+        $this->mailer->send($message);
483
+
484
+        $this->createPasswordSendActivity($share, $shareWith, false);
485
+
486
+        return true;
487
+    }
488
+
489
+    /**
490
+     * send auto generated password to the owner. This happens if the admin enforces
491
+     * a password for mail shares and forbid to send the password by mail to the recipient
492
+     *
493
+     * @param IShare $share
494
+     * @param string $password
495
+     * @return bool
496
+     * @throws \Exception
497
+     */
498
+    protected function sendPasswordToOwner(IShare $share, $password) {
499
+
500
+        $filename = $share->getNode()->getName();
501
+        $initiator = $this->userManager->get($share->getSharedBy());
502
+        $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
503
+        $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
504
+        $shareWith = $share->getSharedWith();
505
+
506
+        if ($initiatorEMailAddress === null) {
507
+            throw new \Exception(
508
+                $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.")
509
+            );
510
+        }
511
+
512
+        $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]);
513
+        $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %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()]);
514
+
515
+        $message = $this->mailer->createMessage();
516
+        $emailTemplate = $this->mailer->createEMailTemplate();
517
+
518
+        $emailTemplate->addHeader();
519
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
520
+        $emailTemplate->addBodyText($bodyPart);
521
+        $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
522
+        $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
523
+        $emailTemplate->addFooter();
524
+
525
+        if ($initiatorEMailAddress) {
526
+            $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
527
+        }
528
+        $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
529
+        $message->setSubject($subject);
530
+        $message->setBody($emailTemplate->renderText(), 'text/plain');
531
+        $message->setHtmlBody($emailTemplate->renderHtml());
532
+        $this->mailer->send($message);
533
+
534
+        $this->createPasswordSendActivity($share, $shareWith, true);
535
+
536
+        return true;
537
+    }
538
+
539
+    /**
540
+     * generate share token
541
+     *
542
+     * @return string
543
+     */
544
+    protected function generateToken($size = 15) {
545
+        $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
546
+        return $token;
547
+    }
548
+
549
+    /**
550
+     * Get all children of this share
551
+     *
552
+     * @param IShare $parent
553
+     * @return IShare[]
554
+     */
555
+    public function getChildren(IShare $parent) {
556
+        $children = [];
557
+
558
+        $qb = $this->dbConnection->getQueryBuilder();
559
+        $qb->select('*')
560
+            ->from('share')
561
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
562
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
563
+            ->orderBy('id');
564
+
565
+        $cursor = $qb->execute();
566
+        while($data = $cursor->fetch()) {
567
+            $children[] = $this->createShareObject($data);
568
+        }
569
+        $cursor->closeCursor();
570
+
571
+        return $children;
572
+    }
573
+
574
+    /**
575
+     * add share to the database and return the ID
576
+     *
577
+     * @param int $itemSource
578
+     * @param string $itemType
579
+     * @param string $shareWith
580
+     * @param string $sharedBy
581
+     * @param string $uidOwner
582
+     * @param int $permissions
583
+     * @param string $token
584
+     * @return int
585
+     */
586
+    protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
587
+        $qb = $this->dbConnection->getQueryBuilder();
588
+        $qb->insert('share')
589
+            ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
590
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
591
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
592
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
593
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
594
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
595
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
596
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
597
+            ->setValue('token', $qb->createNamedParameter($token))
598
+            ->setValue('password', $qb->createNamedParameter($password))
599
+            ->setValue('stime', $qb->createNamedParameter(time()));
600
+
601
+        /*
602 602
 		 * Added to fix https://github.com/owncloud/core/issues/22215
603 603
 		 * Can be removed once we get rid of ajax/share.php
604 604
 		 */
605
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
605
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
606 606
 
607
-		$qb->execute();
608
-		$id = $qb->getLastInsertId();
607
+        $qb->execute();
608
+        $id = $qb->getLastInsertId();
609 609
 
610
-		return (int)$id;
611
-	}
610
+        return (int)$id;
611
+    }
612 612
 
613
-	/**
614
-	 * Update a share
615
-	 *
616
-	 * @param IShare $share
617
-	 * @param string|null $plainTextPassword
618
-	 * @return IShare The share object
619
-	 */
620
-	public function update(IShare $share, $plainTextPassword = null) {
613
+    /**
614
+     * Update a share
615
+     *
616
+     * @param IShare $share
617
+     * @param string|null $plainTextPassword
618
+     * @return IShare The share object
619
+     */
620
+    public function update(IShare $share, $plainTextPassword = null) {
621 621
 
622
-		$originalShare = $this->getShareById($share->getId());
622
+        $originalShare = $this->getShareById($share->getId());
623 623
 
624
-		// a real password was given
625
-		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
624
+        // a real password was given
625
+        $validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
626 626
 
627
-		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
628
-			$this->sendPassword($share, $plainTextPassword);
629
-		}
630
-		/*
627
+        if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
628
+            $this->sendPassword($share, $plainTextPassword);
629
+        }
630
+        /*
631 631
 		 * We allow updating the permissions and password of mail shares
632 632
 		 */
633
-		$qb = $this->dbConnection->getQueryBuilder();
634
-		$qb->update('share')
635
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
636
-			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
637
-			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
638
-			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
639
-			->set('password', $qb->createNamedParameter($share->getPassword()))
640
-			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
641
-			->execute();
642
-
643
-		return $share;
644
-	}
645
-
646
-	/**
647
-	 * @inheritdoc
648
-	 */
649
-	public function move(IShare $share, $recipient) {
650
-		/**
651
-		 * nothing to do here, mail shares are only outgoing shares
652
-		 */
653
-		return $share;
654
-	}
655
-
656
-	/**
657
-	 * Delete a share (owner unShares the file)
658
-	 *
659
-	 * @param IShare $share
660
-	 */
661
-	public function delete(IShare $share) {
662
-		$this->removeShareFromTable($share->getId());
663
-	}
664
-
665
-	/**
666
-	 * @inheritdoc
667
-	 */
668
-	public function deleteFromSelf(IShare $share, $recipient) {
669
-		// nothing to do here, mail shares are only outgoing shares
670
-		return;
671
-	}
672
-
673
-	/**
674
-	 * @inheritdoc
675
-	 */
676
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
677
-		$qb = $this->dbConnection->getQueryBuilder();
678
-		$qb->select('*')
679
-			->from('share');
680
-
681
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
682
-
683
-		/**
684
-		 * Reshares for this user are shares where they are the owner.
685
-		 */
686
-		if ($reshares === false) {
687
-			//Special case for old shares created via the web UI
688
-			$or1 = $qb->expr()->andX(
689
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
690
-				$qb->expr()->isNull('uid_initiator')
691
-			);
692
-
693
-			$qb->andWhere(
694
-				$qb->expr()->orX(
695
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
696
-					$or1
697
-				)
698
-			);
699
-		} else {
700
-			$qb->andWhere(
701
-				$qb->expr()->orX(
702
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
703
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
704
-				)
705
-			);
706
-		}
707
-
708
-		if ($node !== null) {
709
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
710
-		}
711
-
712
-		if ($limit !== -1) {
713
-			$qb->setMaxResults($limit);
714
-		}
715
-
716
-		$qb->setFirstResult($offset);
717
-		$qb->orderBy('id');
718
-
719
-		$cursor = $qb->execute();
720
-		$shares = [];
721
-		while($data = $cursor->fetch()) {
722
-			$shares[] = $this->createShareObject($data);
723
-		}
724
-		$cursor->closeCursor();
725
-
726
-		return $shares;
727
-	}
728
-
729
-	/**
730
-	 * @inheritdoc
731
-	 */
732
-	public function getShareById($id, $recipientId = null) {
733
-		$qb = $this->dbConnection->getQueryBuilder();
734
-
735
-		$qb->select('*')
736
-			->from('share')
737
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
738
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
739
-
740
-		$cursor = $qb->execute();
741
-		$data = $cursor->fetch();
742
-		$cursor->closeCursor();
743
-
744
-		if ($data === false) {
745
-			throw new ShareNotFound();
746
-		}
747
-
748
-		try {
749
-			$share = $this->createShareObject($data);
750
-		} catch (InvalidShare $e) {
751
-			throw new ShareNotFound();
752
-		}
753
-
754
-		return $share;
755
-	}
756
-
757
-	/**
758
-	 * Get shares for a given path
759
-	 *
760
-	 * @param \OCP\Files\Node $path
761
-	 * @return IShare[]
762
-	 */
763
-	public function getSharesByPath(Node $path) {
764
-		$qb = $this->dbConnection->getQueryBuilder();
765
-
766
-		$cursor = $qb->select('*')
767
-			->from('share')
768
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
769
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
770
-			->execute();
771
-
772
-		$shares = [];
773
-		while($data = $cursor->fetch()) {
774
-			$shares[] = $this->createShareObject($data);
775
-		}
776
-		$cursor->closeCursor();
777
-
778
-		return $shares;
779
-	}
780
-
781
-	/**
782
-	 * @inheritdoc
783
-	 */
784
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
785
-		/** @var IShare[] $shares */
786
-		$shares = [];
787
-
788
-		//Get shares directly with this user
789
-		$qb = $this->dbConnection->getQueryBuilder();
790
-		$qb->select('*')
791
-			->from('share');
792
-
793
-		// Order by id
794
-		$qb->orderBy('id');
795
-
796
-		// Set limit and offset
797
-		if ($limit !== -1) {
798
-			$qb->setMaxResults($limit);
799
-		}
800
-		$qb->setFirstResult($offset);
801
-
802
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
803
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
804
-
805
-		// Filter by node if provided
806
-		if ($node !== null) {
807
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
808
-		}
809
-
810
-		$cursor = $qb->execute();
811
-
812
-		while($data = $cursor->fetch()) {
813
-			$shares[] = $this->createShareObject($data);
814
-		}
815
-		$cursor->closeCursor();
816
-
817
-
818
-		return $shares;
819
-	}
820
-
821
-	/**
822
-	 * Get a share by token
823
-	 *
824
-	 * @param string $token
825
-	 * @return IShare
826
-	 * @throws ShareNotFound
827
-	 */
828
-	public function getShareByToken($token) {
829
-		$qb = $this->dbConnection->getQueryBuilder();
830
-
831
-		$cursor = $qb->select('*')
832
-			->from('share')
833
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
834
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
835
-			->execute();
836
-
837
-		$data = $cursor->fetch();
838
-
839
-		if ($data === false) {
840
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
841
-		}
842
-
843
-		try {
844
-			$share = $this->createShareObject($data);
845
-		} catch (InvalidShare $e) {
846
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
847
-		}
848
-
849
-		return $share;
850
-	}
851
-
852
-	/**
853
-	 * remove share from table
854
-	 *
855
-	 * @param string $shareId
856
-	 */
857
-	protected function removeShareFromTable($shareId) {
858
-		$qb = $this->dbConnection->getQueryBuilder();
859
-		$qb->delete('share')
860
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
861
-		$qb->execute();
862
-	}
863
-
864
-	/**
865
-	 * Create a share object from an database row
866
-	 *
867
-	 * @param array $data
868
-	 * @return IShare
869
-	 * @throws InvalidShare
870
-	 * @throws ShareNotFound
871
-	 */
872
-	protected function createShareObject($data) {
873
-
874
-		$share = new Share($this->rootFolder, $this->userManager);
875
-		$share->setId((int)$data['id'])
876
-			->setShareType((int)$data['share_type'])
877
-			->setPermissions((int)$data['permissions'])
878
-			->setTarget($data['file_target'])
879
-			->setMailSend((bool)$data['mail_send'])
880
-			->setToken($data['token']);
881
-
882
-		$shareTime = new \DateTime();
883
-		$shareTime->setTimestamp((int)$data['stime']);
884
-		$share->setShareTime($shareTime);
885
-		$share->setSharedWith($data['share_with']);
886
-		$share->setPassword($data['password']);
887
-
888
-		if ($data['uid_initiator'] !== null) {
889
-			$share->setShareOwner($data['uid_owner']);
890
-			$share->setSharedBy($data['uid_initiator']);
891
-		} else {
892
-			//OLD SHARE
893
-			$share->setSharedBy($data['uid_owner']);
894
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
895
-
896
-			$owner = $path->getOwner();
897
-			$share->setShareOwner($owner->getUID());
898
-		}
899
-
900
-		if ($data['expiration'] !== null) {
901
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
902
-			if ($expiration !== false) {
903
-				$share->setExpirationDate($expiration);
904
-			}
905
-		}
906
-
907
-		$share->setNodeId((int)$data['file_source']);
908
-		$share->setNodeType($data['item_type']);
909
-
910
-		$share->setProviderId($this->identifier());
911
-
912
-		return $share;
913
-	}
914
-
915
-	/**
916
-	 * Get the node with file $id for $user
917
-	 *
918
-	 * @param string $userId
919
-	 * @param int $id
920
-	 * @return \OCP\Files\File|\OCP\Files\Folder
921
-	 * @throws InvalidShare
922
-	 */
923
-	private function getNode($userId, $id) {
924
-		try {
925
-			$userFolder = $this->rootFolder->getUserFolder($userId);
926
-		} catch (NoUserException $e) {
927
-			throw new InvalidShare();
928
-		}
929
-
930
-		$nodes = $userFolder->getById($id);
931
-
932
-		if (empty($nodes)) {
933
-			throw new InvalidShare();
934
-		}
935
-
936
-		return $nodes[0];
937
-	}
938
-
939
-	/**
940
-	 * A user is deleted from the system
941
-	 * So clean up the relevant shares.
942
-	 *
943
-	 * @param string $uid
944
-	 * @param int $shareType
945
-	 */
946
-	public function userDeleted($uid, $shareType) {
947
-		$qb = $this->dbConnection->getQueryBuilder();
948
-
949
-		$qb->delete('share')
950
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
951
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
952
-			->execute();
953
-	}
954
-
955
-	/**
956
-	 * This provider does not support group shares
957
-	 *
958
-	 * @param string $gid
959
-	 */
960
-	public function groupDeleted($gid) {
961
-		return;
962
-	}
963
-
964
-	/**
965
-	 * This provider does not support group shares
966
-	 *
967
-	 * @param string $uid
968
-	 * @param string $gid
969
-	 */
970
-	public function userDeletedFromGroup($uid, $gid) {
971
-		return;
972
-	}
973
-
974
-	/**
975
-	 * get database row of a give share
976
-	 *
977
-	 * @param $id
978
-	 * @return array
979
-	 * @throws ShareNotFound
980
-	 */
981
-	protected function getRawShare($id) {
982
-
983
-		// Now fetch the inserted share and create a complete share object
984
-		$qb = $this->dbConnection->getQueryBuilder();
985
-		$qb->select('*')
986
-			->from('share')
987
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
988
-
989
-		$cursor = $qb->execute();
990
-		$data = $cursor->fetch();
991
-		$cursor->closeCursor();
992
-
993
-		if ($data === false) {
994
-			throw new ShareNotFound;
995
-		}
996
-
997
-		return $data;
998
-	}
999
-
1000
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
1001
-		$qb = $this->dbConnection->getQueryBuilder();
1002
-		$qb->select('*')
1003
-			->from('share', 's')
1004
-			->andWhere($qb->expr()->orX(
1005
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1006
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1007
-			))
1008
-			->andWhere(
1009
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1010
-			);
1011
-
1012
-		/**
1013
-		 * Reshares for this user are shares where they are the owner.
1014
-		 */
1015
-		if ($reshares === false) {
1016
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1017
-		} else {
1018
-			$qb->andWhere(
1019
-				$qb->expr()->orX(
1020
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1021
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1022
-				)
1023
-			);
1024
-		}
1025
-
1026
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1027
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1028
-
1029
-		$qb->orderBy('id');
1030
-
1031
-		$cursor = $qb->execute();
1032
-		$shares = [];
1033
-		while ($data = $cursor->fetch()) {
1034
-			$shares[$data['fileid']][] = $this->createShareObject($data);
1035
-		}
1036
-		$cursor->closeCursor();
1037
-
1038
-		return $shares;
1039
-	}
1040
-
1041
-	/**
1042
-	 * @inheritdoc
1043
-	 */
1044
-	public function getAccessList($nodes, $currentAccess) {
1045
-		$ids = [];
1046
-		foreach ($nodes as $node) {
1047
-			$ids[] = $node->getId();
1048
-		}
1049
-
1050
-		$qb = $this->dbConnection->getQueryBuilder();
1051
-		$qb->select('share_with')
1052
-			->from('share')
1053
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1054
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1055
-			->andWhere($qb->expr()->orX(
1056
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1057
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1058
-			))
1059
-			->setMaxResults(1);
1060
-		$cursor = $qb->execute();
1061
-
1062
-		$mail = $cursor->fetch() !== false;
1063
-		$cursor->closeCursor();
1064
-
1065
-		return ['public' => $mail];
1066
-	}
633
+        $qb = $this->dbConnection->getQueryBuilder();
634
+        $qb->update('share')
635
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
636
+            ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
637
+            ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
638
+            ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
639
+            ->set('password', $qb->createNamedParameter($share->getPassword()))
640
+            ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
641
+            ->execute();
642
+
643
+        return $share;
644
+    }
645
+
646
+    /**
647
+     * @inheritdoc
648
+     */
649
+    public function move(IShare $share, $recipient) {
650
+        /**
651
+         * nothing to do here, mail shares are only outgoing shares
652
+         */
653
+        return $share;
654
+    }
655
+
656
+    /**
657
+     * Delete a share (owner unShares the file)
658
+     *
659
+     * @param IShare $share
660
+     */
661
+    public function delete(IShare $share) {
662
+        $this->removeShareFromTable($share->getId());
663
+    }
664
+
665
+    /**
666
+     * @inheritdoc
667
+     */
668
+    public function deleteFromSelf(IShare $share, $recipient) {
669
+        // nothing to do here, mail shares are only outgoing shares
670
+        return;
671
+    }
672
+
673
+    /**
674
+     * @inheritdoc
675
+     */
676
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
677
+        $qb = $this->dbConnection->getQueryBuilder();
678
+        $qb->select('*')
679
+            ->from('share');
680
+
681
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
682
+
683
+        /**
684
+         * Reshares for this user are shares where they are the owner.
685
+         */
686
+        if ($reshares === false) {
687
+            //Special case for old shares created via the web UI
688
+            $or1 = $qb->expr()->andX(
689
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
690
+                $qb->expr()->isNull('uid_initiator')
691
+            );
692
+
693
+            $qb->andWhere(
694
+                $qb->expr()->orX(
695
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
696
+                    $or1
697
+                )
698
+            );
699
+        } else {
700
+            $qb->andWhere(
701
+                $qb->expr()->orX(
702
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
703
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
704
+                )
705
+            );
706
+        }
707
+
708
+        if ($node !== null) {
709
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
710
+        }
711
+
712
+        if ($limit !== -1) {
713
+            $qb->setMaxResults($limit);
714
+        }
715
+
716
+        $qb->setFirstResult($offset);
717
+        $qb->orderBy('id');
718
+
719
+        $cursor = $qb->execute();
720
+        $shares = [];
721
+        while($data = $cursor->fetch()) {
722
+            $shares[] = $this->createShareObject($data);
723
+        }
724
+        $cursor->closeCursor();
725
+
726
+        return $shares;
727
+    }
728
+
729
+    /**
730
+     * @inheritdoc
731
+     */
732
+    public function getShareById($id, $recipientId = null) {
733
+        $qb = $this->dbConnection->getQueryBuilder();
734
+
735
+        $qb->select('*')
736
+            ->from('share')
737
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
738
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
739
+
740
+        $cursor = $qb->execute();
741
+        $data = $cursor->fetch();
742
+        $cursor->closeCursor();
743
+
744
+        if ($data === false) {
745
+            throw new ShareNotFound();
746
+        }
747
+
748
+        try {
749
+            $share = $this->createShareObject($data);
750
+        } catch (InvalidShare $e) {
751
+            throw new ShareNotFound();
752
+        }
753
+
754
+        return $share;
755
+    }
756
+
757
+    /**
758
+     * Get shares for a given path
759
+     *
760
+     * @param \OCP\Files\Node $path
761
+     * @return IShare[]
762
+     */
763
+    public function getSharesByPath(Node $path) {
764
+        $qb = $this->dbConnection->getQueryBuilder();
765
+
766
+        $cursor = $qb->select('*')
767
+            ->from('share')
768
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
769
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
770
+            ->execute();
771
+
772
+        $shares = [];
773
+        while($data = $cursor->fetch()) {
774
+            $shares[] = $this->createShareObject($data);
775
+        }
776
+        $cursor->closeCursor();
777
+
778
+        return $shares;
779
+    }
780
+
781
+    /**
782
+     * @inheritdoc
783
+     */
784
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
785
+        /** @var IShare[] $shares */
786
+        $shares = [];
787
+
788
+        //Get shares directly with this user
789
+        $qb = $this->dbConnection->getQueryBuilder();
790
+        $qb->select('*')
791
+            ->from('share');
792
+
793
+        // Order by id
794
+        $qb->orderBy('id');
795
+
796
+        // Set limit and offset
797
+        if ($limit !== -1) {
798
+            $qb->setMaxResults($limit);
799
+        }
800
+        $qb->setFirstResult($offset);
801
+
802
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
803
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
804
+
805
+        // Filter by node if provided
806
+        if ($node !== null) {
807
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
808
+        }
809
+
810
+        $cursor = $qb->execute();
811
+
812
+        while($data = $cursor->fetch()) {
813
+            $shares[] = $this->createShareObject($data);
814
+        }
815
+        $cursor->closeCursor();
816
+
817
+
818
+        return $shares;
819
+    }
820
+
821
+    /**
822
+     * Get a share by token
823
+     *
824
+     * @param string $token
825
+     * @return IShare
826
+     * @throws ShareNotFound
827
+     */
828
+    public function getShareByToken($token) {
829
+        $qb = $this->dbConnection->getQueryBuilder();
830
+
831
+        $cursor = $qb->select('*')
832
+            ->from('share')
833
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
834
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
835
+            ->execute();
836
+
837
+        $data = $cursor->fetch();
838
+
839
+        if ($data === false) {
840
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
841
+        }
842
+
843
+        try {
844
+            $share = $this->createShareObject($data);
845
+        } catch (InvalidShare $e) {
846
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
847
+        }
848
+
849
+        return $share;
850
+    }
851
+
852
+    /**
853
+     * remove share from table
854
+     *
855
+     * @param string $shareId
856
+     */
857
+    protected function removeShareFromTable($shareId) {
858
+        $qb = $this->dbConnection->getQueryBuilder();
859
+        $qb->delete('share')
860
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
861
+        $qb->execute();
862
+    }
863
+
864
+    /**
865
+     * Create a share object from an database row
866
+     *
867
+     * @param array $data
868
+     * @return IShare
869
+     * @throws InvalidShare
870
+     * @throws ShareNotFound
871
+     */
872
+    protected function createShareObject($data) {
873
+
874
+        $share = new Share($this->rootFolder, $this->userManager);
875
+        $share->setId((int)$data['id'])
876
+            ->setShareType((int)$data['share_type'])
877
+            ->setPermissions((int)$data['permissions'])
878
+            ->setTarget($data['file_target'])
879
+            ->setMailSend((bool)$data['mail_send'])
880
+            ->setToken($data['token']);
881
+
882
+        $shareTime = new \DateTime();
883
+        $shareTime->setTimestamp((int)$data['stime']);
884
+        $share->setShareTime($shareTime);
885
+        $share->setSharedWith($data['share_with']);
886
+        $share->setPassword($data['password']);
887
+
888
+        if ($data['uid_initiator'] !== null) {
889
+            $share->setShareOwner($data['uid_owner']);
890
+            $share->setSharedBy($data['uid_initiator']);
891
+        } else {
892
+            //OLD SHARE
893
+            $share->setSharedBy($data['uid_owner']);
894
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
895
+
896
+            $owner = $path->getOwner();
897
+            $share->setShareOwner($owner->getUID());
898
+        }
899
+
900
+        if ($data['expiration'] !== null) {
901
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
902
+            if ($expiration !== false) {
903
+                $share->setExpirationDate($expiration);
904
+            }
905
+        }
906
+
907
+        $share->setNodeId((int)$data['file_source']);
908
+        $share->setNodeType($data['item_type']);
909
+
910
+        $share->setProviderId($this->identifier());
911
+
912
+        return $share;
913
+    }
914
+
915
+    /**
916
+     * Get the node with file $id for $user
917
+     *
918
+     * @param string $userId
919
+     * @param int $id
920
+     * @return \OCP\Files\File|\OCP\Files\Folder
921
+     * @throws InvalidShare
922
+     */
923
+    private function getNode($userId, $id) {
924
+        try {
925
+            $userFolder = $this->rootFolder->getUserFolder($userId);
926
+        } catch (NoUserException $e) {
927
+            throw new InvalidShare();
928
+        }
929
+
930
+        $nodes = $userFolder->getById($id);
931
+
932
+        if (empty($nodes)) {
933
+            throw new InvalidShare();
934
+        }
935
+
936
+        return $nodes[0];
937
+    }
938
+
939
+    /**
940
+     * A user is deleted from the system
941
+     * So clean up the relevant shares.
942
+     *
943
+     * @param string $uid
944
+     * @param int $shareType
945
+     */
946
+    public function userDeleted($uid, $shareType) {
947
+        $qb = $this->dbConnection->getQueryBuilder();
948
+
949
+        $qb->delete('share')
950
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
951
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
952
+            ->execute();
953
+    }
954
+
955
+    /**
956
+     * This provider does not support group shares
957
+     *
958
+     * @param string $gid
959
+     */
960
+    public function groupDeleted($gid) {
961
+        return;
962
+    }
963
+
964
+    /**
965
+     * This provider does not support group shares
966
+     *
967
+     * @param string $uid
968
+     * @param string $gid
969
+     */
970
+    public function userDeletedFromGroup($uid, $gid) {
971
+        return;
972
+    }
973
+
974
+    /**
975
+     * get database row of a give share
976
+     *
977
+     * @param $id
978
+     * @return array
979
+     * @throws ShareNotFound
980
+     */
981
+    protected function getRawShare($id) {
982
+
983
+        // Now fetch the inserted share and create a complete share object
984
+        $qb = $this->dbConnection->getQueryBuilder();
985
+        $qb->select('*')
986
+            ->from('share')
987
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
988
+
989
+        $cursor = $qb->execute();
990
+        $data = $cursor->fetch();
991
+        $cursor->closeCursor();
992
+
993
+        if ($data === false) {
994
+            throw new ShareNotFound;
995
+        }
996
+
997
+        return $data;
998
+    }
999
+
1000
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
1001
+        $qb = $this->dbConnection->getQueryBuilder();
1002
+        $qb->select('*')
1003
+            ->from('share', 's')
1004
+            ->andWhere($qb->expr()->orX(
1005
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1006
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1007
+            ))
1008
+            ->andWhere(
1009
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1010
+            );
1011
+
1012
+        /**
1013
+         * Reshares for this user are shares where they are the owner.
1014
+         */
1015
+        if ($reshares === false) {
1016
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1017
+        } else {
1018
+            $qb->andWhere(
1019
+                $qb->expr()->orX(
1020
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1021
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1022
+                )
1023
+            );
1024
+        }
1025
+
1026
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1027
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1028
+
1029
+        $qb->orderBy('id');
1030
+
1031
+        $cursor = $qb->execute();
1032
+        $shares = [];
1033
+        while ($data = $cursor->fetch()) {
1034
+            $shares[$data['fileid']][] = $this->createShareObject($data);
1035
+        }
1036
+        $cursor->closeCursor();
1037
+
1038
+        return $shares;
1039
+    }
1040
+
1041
+    /**
1042
+     * @inheritdoc
1043
+     */
1044
+    public function getAccessList($nodes, $currentAccess) {
1045
+        $ids = [];
1046
+        foreach ($nodes as $node) {
1047
+            $ids[] = $node->getId();
1048
+        }
1049
+
1050
+        $qb = $this->dbConnection->getQueryBuilder();
1051
+        $qb->select('share_with')
1052
+            ->from('share')
1053
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1054
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1055
+            ->andWhere($qb->expr()->orX(
1056
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1057
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1058
+            ))
1059
+            ->setMaxResults(1);
1060
+        $cursor = $qb->execute();
1061
+
1062
+        $mail = $cursor->fetch() !== false;
1063
+        $cursor->closeCursor();
1064
+
1065
+        return ['public' => $mail];
1066
+    }
1067 1067
 
1068 1068
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/CalDavBackend.php 1 patch
Indentation   +2056 added lines, -2056 removed lines patch added patch discarded remove patch
@@ -62,2061 +62,2061 @@
 block discarded – undo
62 62
  */
63 63
 class CalDavBackend extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport {
64 64
 
65
-	const PERSONAL_CALENDAR_URI = 'personal';
66
-	const PERSONAL_CALENDAR_NAME = 'Personal';
67
-
68
-	/**
69
-	 * We need to specify a max date, because we need to stop *somewhere*
70
-	 *
71
-	 * On 32 bit system the maximum for a signed integer is 2147483647, so
72
-	 * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
73
-	 * in 2038-01-19 to avoid problems when the date is converted
74
-	 * to a unix timestamp.
75
-	 */
76
-	const MAX_DATE = '2038-01-01';
77
-
78
-	const ACCESS_PUBLIC = 4;
79
-	const CLASSIFICATION_PUBLIC = 0;
80
-	const CLASSIFICATION_PRIVATE = 1;
81
-	const CLASSIFICATION_CONFIDENTIAL = 2;
82
-
83
-	/**
84
-	 * List of CalDAV properties, and how they map to database field names
85
-	 * Add your own properties by simply adding on to this array.
86
-	 *
87
-	 * Note that only string-based properties are supported here.
88
-	 *
89
-	 * @var array
90
-	 */
91
-	public $propertyMap = [
92
-		'{DAV:}displayname'                          => 'displayname',
93
-		'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
94
-		'{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
95
-		'{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
96
-		'{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
97
-	];
98
-
99
-	/**
100
-	 * List of subscription properties, and how they map to database field names.
101
-	 *
102
-	 * @var array
103
-	 */
104
-	public $subscriptionPropertyMap = [
105
-		'{DAV:}displayname'                                           => 'displayname',
106
-		'{http://apple.com/ns/ical/}refreshrate'                      => 'refreshrate',
107
-		'{http://apple.com/ns/ical/}calendar-order'                   => 'calendarorder',
108
-		'{http://apple.com/ns/ical/}calendar-color'                   => 'calendarcolor',
109
-		'{http://calendarserver.org/ns/}subscribed-strip-todos'       => 'striptodos',
110
-		'{http://calendarserver.org/ns/}subscribed-strip-alarms'      => 'stripalarms',
111
-		'{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
112
-	];
113
-
114
-	/** @var array properties to index */
115
-	public static $indexProperties = ['CATEGORIES', 'COMMENT', 'DESCRIPTION',
116
-		'LOCATION', 'RESOURCES', 'STATUS', 'SUMMARY', 'ATTENDEE', 'CONTACT',
117
-		'ORGANIZER'];
118
-
119
-	/** @var array parameters to index */
120
-	public static $indexParameters = [
121
-		'ATTENDEE' => ['CN'],
122
-		'ORGANIZER' => ['CN'],
123
-	];
124
-
125
-	/**
126
-	 * @var string[] Map of uid => display name
127
-	 */
128
-	protected $userDisplayNames;
129
-
130
-	/** @var IDBConnection */
131
-	private $db;
132
-
133
-	/** @var Backend */
134
-	private $sharingBackend;
135
-
136
-	/** @var Principal */
137
-	private $principalBackend;
138
-
139
-	/** @var IUserManager */
140
-	private $userManager;
141
-
142
-	/** @var ISecureRandom */
143
-	private $random;
144
-
145
-	/** @var EventDispatcherInterface */
146
-	private $dispatcher;
147
-
148
-	/** @var bool */
149
-	private $legacyEndpoint;
150
-
151
-	/** @var string */
152
-	private $dbObjectPropertiesTable = 'calendarobjects_props';
153
-
154
-	/**
155
-	 * CalDavBackend constructor.
156
-	 *
157
-	 * @param IDBConnection $db
158
-	 * @param Principal $principalBackend
159
-	 * @param IUserManager $userManager
160
-	 * @param ISecureRandom $random
161
-	 * @param EventDispatcherInterface $dispatcher
162
-	 * @param bool $legacyEndpoint
163
-	 */
164
-	public function __construct(IDBConnection $db,
165
-								Principal $principalBackend,
166
-								IUserManager $userManager,
167
-								ISecureRandom $random,
168
-								EventDispatcherInterface $dispatcher,
169
-								$legacyEndpoint = false) {
170
-		$this->db = $db;
171
-		$this->principalBackend = $principalBackend;
172
-		$this->userManager = $userManager;
173
-		$this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar');
174
-		$this->random = $random;
175
-		$this->dispatcher = $dispatcher;
176
-		$this->legacyEndpoint = $legacyEndpoint;
177
-	}
178
-
179
-	/**
180
-	 * Return the number of calendars for a principal
181
-	 *
182
-	 * By default this excludes the automatically generated birthday calendar
183
-	 *
184
-	 * @param $principalUri
185
-	 * @param bool $excludeBirthday
186
-	 * @return int
187
-	 */
188
-	public function getCalendarsForUserCount($principalUri, $excludeBirthday = true) {
189
-		$principalUri = $this->convertPrincipal($principalUri, true);
190
-		$query = $this->db->getQueryBuilder();
191
-		$query->select($query->createFunction('COUNT(*)'))
192
-			->from('calendars')
193
-			->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)));
194
-
195
-		if ($excludeBirthday) {
196
-			$query->andWhere($query->expr()->neq('uri', $query->createNamedParameter(BirthdayService::BIRTHDAY_CALENDAR_URI)));
197
-		}
198
-
199
-		return (int)$query->execute()->fetchColumn();
200
-	}
201
-
202
-	/**
203
-	 * Returns a list of calendars for a principal.
204
-	 *
205
-	 * Every project is an array with the following keys:
206
-	 *  * id, a unique id that will be used by other functions to modify the
207
-	 *    calendar. This can be the same as the uri or a database key.
208
-	 *  * uri, which the basename of the uri with which the calendar is
209
-	 *    accessed.
210
-	 *  * principaluri. The owner of the calendar. Almost always the same as
211
-	 *    principalUri passed to this method.
212
-	 *
213
-	 * Furthermore it can contain webdav properties in clark notation. A very
214
-	 * common one is '{DAV:}displayname'.
215
-	 *
216
-	 * Many clients also require:
217
-	 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
218
-	 * For this property, you can just return an instance of
219
-	 * Sabre\CalDAV\Property\SupportedCalendarComponentSet.
220
-	 *
221
-	 * If you return {http://sabredav.org/ns}read-only and set the value to 1,
222
-	 * ACL will automatically be put in read-only mode.
223
-	 *
224
-	 * @param string $principalUri
225
-	 * @return array
226
-	 */
227
-	function getCalendarsForUser($principalUri) {
228
-		$principalUriOriginal = $principalUri;
229
-		$principalUri = $this->convertPrincipal($principalUri, true);
230
-		$fields = array_values($this->propertyMap);
231
-		$fields[] = 'id';
232
-		$fields[] = 'uri';
233
-		$fields[] = 'synctoken';
234
-		$fields[] = 'components';
235
-		$fields[] = 'principaluri';
236
-		$fields[] = 'transparent';
237
-
238
-		// Making fields a comma-delimited list
239
-		$query = $this->db->getQueryBuilder();
240
-		$query->select($fields)->from('calendars')
241
-				->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
242
-				->orderBy('calendarorder', 'ASC');
243
-		$stmt = $query->execute();
244
-
245
-		$calendars = [];
246
-		while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
247
-
248
-			$components = [];
249
-			if ($row['components']) {
250
-				$components = explode(',',$row['components']);
251
-			}
252
-
253
-			$calendar = [
254
-				'id' => $row['id'],
255
-				'uri' => $row['uri'],
256
-				'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
257
-				'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
258
-				'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
259
-				'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
260
-				'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
261
-				'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint),
262
-			];
263
-
264
-			foreach($this->propertyMap as $xmlName=>$dbName) {
265
-				$calendar[$xmlName] = $row[$dbName];
266
-			}
267
-
268
-			$this->addOwnerPrincipal($calendar);
269
-
270
-			if (!isset($calendars[$calendar['id']])) {
271
-				$calendars[$calendar['id']] = $calendar;
272
-			}
273
-		}
274
-
275
-		$stmt->closeCursor();
276
-
277
-		// query for shared calendars
278
-		$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
279
-		$principals = array_map(function($principal) {
280
-			return urldecode($principal);
281
-		}, $principals);
282
-		$principals[]= $principalUri;
283
-
284
-		$fields = array_values($this->propertyMap);
285
-		$fields[] = 'a.id';
286
-		$fields[] = 'a.uri';
287
-		$fields[] = 'a.synctoken';
288
-		$fields[] = 'a.components';
289
-		$fields[] = 'a.principaluri';
290
-		$fields[] = 'a.transparent';
291
-		$fields[] = 's.access';
292
-		$query = $this->db->getQueryBuilder();
293
-		$result = $query->select($fields)
294
-			->from('dav_shares', 's')
295
-			->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
296
-			->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
297
-			->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
298
-			->setParameter('type', 'calendar')
299
-			->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
300
-			->execute();
301
-
302
-		$readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
303
-		while($row = $result->fetch()) {
304
-			if ($row['principaluri'] === $principalUri) {
305
-				continue;
306
-			}
307
-
308
-			$readOnly = (int) $row['access'] === Backend::ACCESS_READ;
309
-			if (isset($calendars[$row['id']])) {
310
-				if ($readOnly) {
311
-					// New share can not have more permissions then the old one.
312
-					continue;
313
-				}
314
-				if (isset($calendars[$row['id']][$readOnlyPropertyName]) &&
315
-					$calendars[$row['id']][$readOnlyPropertyName] === 0) {
316
-					// Old share is already read-write, no more permissions can be gained
317
-					continue;
318
-				}
319
-			}
320
-
321
-			list(, $name) = URLUtil::splitPath($row['principaluri']);
322
-			$uri = $row['uri'] . '_shared_by_' . $name;
323
-			$row['displayname'] = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')';
324
-			$components = [];
325
-			if ($row['components']) {
326
-				$components = explode(',',$row['components']);
327
-			}
328
-			$calendar = [
329
-				'id' => $row['id'],
330
-				'uri' => $uri,
331
-				'principaluri' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint),
332
-				'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
333
-				'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
334
-				'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
335
-				'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
336
-				'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
337
-				$readOnlyPropertyName => $readOnly,
338
-			];
339
-
340
-			foreach($this->propertyMap as $xmlName=>$dbName) {
341
-				$calendar[$xmlName] = $row[$dbName];
342
-			}
343
-
344
-			$this->addOwnerPrincipal($calendar);
345
-
346
-			$calendars[$calendar['id']] = $calendar;
347
-		}
348
-		$result->closeCursor();
349
-
350
-		return array_values($calendars);
351
-	}
352
-
353
-	public function getUsersOwnCalendars($principalUri) {
354
-		$principalUri = $this->convertPrincipal($principalUri, true);
355
-		$fields = array_values($this->propertyMap);
356
-		$fields[] = 'id';
357
-		$fields[] = 'uri';
358
-		$fields[] = 'synctoken';
359
-		$fields[] = 'components';
360
-		$fields[] = 'principaluri';
361
-		$fields[] = 'transparent';
362
-		// Making fields a comma-delimited list
363
-		$query = $this->db->getQueryBuilder();
364
-		$query->select($fields)->from('calendars')
365
-			->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
366
-			->orderBy('calendarorder', 'ASC');
367
-		$stmt = $query->execute();
368
-		$calendars = [];
369
-		while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
370
-			$components = [];
371
-			if ($row['components']) {
372
-				$components = explode(',',$row['components']);
373
-			}
374
-			$calendar = [
375
-				'id' => $row['id'],
376
-				'uri' => $row['uri'],
377
-				'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
378
-				'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
379
-				'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
380
-				'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
381
-				'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
382
-			];
383
-			foreach($this->propertyMap as $xmlName=>$dbName) {
384
-				$calendar[$xmlName] = $row[$dbName];
385
-			}
386
-
387
-			$this->addOwnerPrincipal($calendar);
388
-
389
-			if (!isset($calendars[$calendar['id']])) {
390
-				$calendars[$calendar['id']] = $calendar;
391
-			}
392
-		}
393
-		$stmt->closeCursor();
394
-		return array_values($calendars);
395
-	}
396
-
397
-
398
-	private function getUserDisplayName($uid) {
399
-		if (!isset($this->userDisplayNames[$uid])) {
400
-			$user = $this->userManager->get($uid);
401
-
402
-			if ($user instanceof IUser) {
403
-				$this->userDisplayNames[$uid] = $user->getDisplayName();
404
-			} else {
405
-				$this->userDisplayNames[$uid] = $uid;
406
-			}
407
-		}
408
-
409
-		return $this->userDisplayNames[$uid];
410
-	}
65
+    const PERSONAL_CALENDAR_URI = 'personal';
66
+    const PERSONAL_CALENDAR_NAME = 'Personal';
67
+
68
+    /**
69
+     * We need to specify a max date, because we need to stop *somewhere*
70
+     *
71
+     * On 32 bit system the maximum for a signed integer is 2147483647, so
72
+     * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
73
+     * in 2038-01-19 to avoid problems when the date is converted
74
+     * to a unix timestamp.
75
+     */
76
+    const MAX_DATE = '2038-01-01';
77
+
78
+    const ACCESS_PUBLIC = 4;
79
+    const CLASSIFICATION_PUBLIC = 0;
80
+    const CLASSIFICATION_PRIVATE = 1;
81
+    const CLASSIFICATION_CONFIDENTIAL = 2;
82
+
83
+    /**
84
+     * List of CalDAV properties, and how they map to database field names
85
+     * Add your own properties by simply adding on to this array.
86
+     *
87
+     * Note that only string-based properties are supported here.
88
+     *
89
+     * @var array
90
+     */
91
+    public $propertyMap = [
92
+        '{DAV:}displayname'                          => 'displayname',
93
+        '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
94
+        '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
95
+        '{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
96
+        '{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
97
+    ];
98
+
99
+    /**
100
+     * List of subscription properties, and how they map to database field names.
101
+     *
102
+     * @var array
103
+     */
104
+    public $subscriptionPropertyMap = [
105
+        '{DAV:}displayname'                                           => 'displayname',
106
+        '{http://apple.com/ns/ical/}refreshrate'                      => 'refreshrate',
107
+        '{http://apple.com/ns/ical/}calendar-order'                   => 'calendarorder',
108
+        '{http://apple.com/ns/ical/}calendar-color'                   => 'calendarcolor',
109
+        '{http://calendarserver.org/ns/}subscribed-strip-todos'       => 'striptodos',
110
+        '{http://calendarserver.org/ns/}subscribed-strip-alarms'      => 'stripalarms',
111
+        '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
112
+    ];
113
+
114
+    /** @var array properties to index */
115
+    public static $indexProperties = ['CATEGORIES', 'COMMENT', 'DESCRIPTION',
116
+        'LOCATION', 'RESOURCES', 'STATUS', 'SUMMARY', 'ATTENDEE', 'CONTACT',
117
+        'ORGANIZER'];
118
+
119
+    /** @var array parameters to index */
120
+    public static $indexParameters = [
121
+        'ATTENDEE' => ['CN'],
122
+        'ORGANIZER' => ['CN'],
123
+    ];
124
+
125
+    /**
126
+     * @var string[] Map of uid => display name
127
+     */
128
+    protected $userDisplayNames;
129
+
130
+    /** @var IDBConnection */
131
+    private $db;
132
+
133
+    /** @var Backend */
134
+    private $sharingBackend;
135
+
136
+    /** @var Principal */
137
+    private $principalBackend;
138
+
139
+    /** @var IUserManager */
140
+    private $userManager;
141
+
142
+    /** @var ISecureRandom */
143
+    private $random;
144
+
145
+    /** @var EventDispatcherInterface */
146
+    private $dispatcher;
147
+
148
+    /** @var bool */
149
+    private $legacyEndpoint;
150
+
151
+    /** @var string */
152
+    private $dbObjectPropertiesTable = 'calendarobjects_props';
153
+
154
+    /**
155
+     * CalDavBackend constructor.
156
+     *
157
+     * @param IDBConnection $db
158
+     * @param Principal $principalBackend
159
+     * @param IUserManager $userManager
160
+     * @param ISecureRandom $random
161
+     * @param EventDispatcherInterface $dispatcher
162
+     * @param bool $legacyEndpoint
163
+     */
164
+    public function __construct(IDBConnection $db,
165
+                                Principal $principalBackend,
166
+                                IUserManager $userManager,
167
+                                ISecureRandom $random,
168
+                                EventDispatcherInterface $dispatcher,
169
+                                $legacyEndpoint = false) {
170
+        $this->db = $db;
171
+        $this->principalBackend = $principalBackend;
172
+        $this->userManager = $userManager;
173
+        $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar');
174
+        $this->random = $random;
175
+        $this->dispatcher = $dispatcher;
176
+        $this->legacyEndpoint = $legacyEndpoint;
177
+    }
178
+
179
+    /**
180
+     * Return the number of calendars for a principal
181
+     *
182
+     * By default this excludes the automatically generated birthday calendar
183
+     *
184
+     * @param $principalUri
185
+     * @param bool $excludeBirthday
186
+     * @return int
187
+     */
188
+    public function getCalendarsForUserCount($principalUri, $excludeBirthday = true) {
189
+        $principalUri = $this->convertPrincipal($principalUri, true);
190
+        $query = $this->db->getQueryBuilder();
191
+        $query->select($query->createFunction('COUNT(*)'))
192
+            ->from('calendars')
193
+            ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)));
194
+
195
+        if ($excludeBirthday) {
196
+            $query->andWhere($query->expr()->neq('uri', $query->createNamedParameter(BirthdayService::BIRTHDAY_CALENDAR_URI)));
197
+        }
198
+
199
+        return (int)$query->execute()->fetchColumn();
200
+    }
201
+
202
+    /**
203
+     * Returns a list of calendars for a principal.
204
+     *
205
+     * Every project is an array with the following keys:
206
+     *  * id, a unique id that will be used by other functions to modify the
207
+     *    calendar. This can be the same as the uri or a database key.
208
+     *  * uri, which the basename of the uri with which the calendar is
209
+     *    accessed.
210
+     *  * principaluri. The owner of the calendar. Almost always the same as
211
+     *    principalUri passed to this method.
212
+     *
213
+     * Furthermore it can contain webdav properties in clark notation. A very
214
+     * common one is '{DAV:}displayname'.
215
+     *
216
+     * Many clients also require:
217
+     * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
218
+     * For this property, you can just return an instance of
219
+     * Sabre\CalDAV\Property\SupportedCalendarComponentSet.
220
+     *
221
+     * If you return {http://sabredav.org/ns}read-only and set the value to 1,
222
+     * ACL will automatically be put in read-only mode.
223
+     *
224
+     * @param string $principalUri
225
+     * @return array
226
+     */
227
+    function getCalendarsForUser($principalUri) {
228
+        $principalUriOriginal = $principalUri;
229
+        $principalUri = $this->convertPrincipal($principalUri, true);
230
+        $fields = array_values($this->propertyMap);
231
+        $fields[] = 'id';
232
+        $fields[] = 'uri';
233
+        $fields[] = 'synctoken';
234
+        $fields[] = 'components';
235
+        $fields[] = 'principaluri';
236
+        $fields[] = 'transparent';
237
+
238
+        // Making fields a comma-delimited list
239
+        $query = $this->db->getQueryBuilder();
240
+        $query->select($fields)->from('calendars')
241
+                ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
242
+                ->orderBy('calendarorder', 'ASC');
243
+        $stmt = $query->execute();
244
+
245
+        $calendars = [];
246
+        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
247
+
248
+            $components = [];
249
+            if ($row['components']) {
250
+                $components = explode(',',$row['components']);
251
+            }
252
+
253
+            $calendar = [
254
+                'id' => $row['id'],
255
+                'uri' => $row['uri'],
256
+                'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
257
+                '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
258
+                '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
259
+                '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
260
+                '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
261
+                '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint),
262
+            ];
263
+
264
+            foreach($this->propertyMap as $xmlName=>$dbName) {
265
+                $calendar[$xmlName] = $row[$dbName];
266
+            }
267
+
268
+            $this->addOwnerPrincipal($calendar);
269
+
270
+            if (!isset($calendars[$calendar['id']])) {
271
+                $calendars[$calendar['id']] = $calendar;
272
+            }
273
+        }
274
+
275
+        $stmt->closeCursor();
276
+
277
+        // query for shared calendars
278
+        $principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
279
+        $principals = array_map(function($principal) {
280
+            return urldecode($principal);
281
+        }, $principals);
282
+        $principals[]= $principalUri;
283
+
284
+        $fields = array_values($this->propertyMap);
285
+        $fields[] = 'a.id';
286
+        $fields[] = 'a.uri';
287
+        $fields[] = 'a.synctoken';
288
+        $fields[] = 'a.components';
289
+        $fields[] = 'a.principaluri';
290
+        $fields[] = 'a.transparent';
291
+        $fields[] = 's.access';
292
+        $query = $this->db->getQueryBuilder();
293
+        $result = $query->select($fields)
294
+            ->from('dav_shares', 's')
295
+            ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
296
+            ->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
297
+            ->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
298
+            ->setParameter('type', 'calendar')
299
+            ->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
300
+            ->execute();
301
+
302
+        $readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
303
+        while($row = $result->fetch()) {
304
+            if ($row['principaluri'] === $principalUri) {
305
+                continue;
306
+            }
307
+
308
+            $readOnly = (int) $row['access'] === Backend::ACCESS_READ;
309
+            if (isset($calendars[$row['id']])) {
310
+                if ($readOnly) {
311
+                    // New share can not have more permissions then the old one.
312
+                    continue;
313
+                }
314
+                if (isset($calendars[$row['id']][$readOnlyPropertyName]) &&
315
+                    $calendars[$row['id']][$readOnlyPropertyName] === 0) {
316
+                    // Old share is already read-write, no more permissions can be gained
317
+                    continue;
318
+                }
319
+            }
320
+
321
+            list(, $name) = URLUtil::splitPath($row['principaluri']);
322
+            $uri = $row['uri'] . '_shared_by_' . $name;
323
+            $row['displayname'] = $row['displayname'] . ' (' . $this->getUserDisplayName($name) . ')';
324
+            $components = [];
325
+            if ($row['components']) {
326
+                $components = explode(',',$row['components']);
327
+            }
328
+            $calendar = [
329
+                'id' => $row['id'],
330
+                'uri' => $uri,
331
+                'principaluri' => $this->convertPrincipal($principalUri, !$this->legacyEndpoint),
332
+                '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
333
+                '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
334
+                '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
335
+                '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
336
+                '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
337
+                $readOnlyPropertyName => $readOnly,
338
+            ];
339
+
340
+            foreach($this->propertyMap as $xmlName=>$dbName) {
341
+                $calendar[$xmlName] = $row[$dbName];
342
+            }
343
+
344
+            $this->addOwnerPrincipal($calendar);
345
+
346
+            $calendars[$calendar['id']] = $calendar;
347
+        }
348
+        $result->closeCursor();
349
+
350
+        return array_values($calendars);
351
+    }
352
+
353
+    public function getUsersOwnCalendars($principalUri) {
354
+        $principalUri = $this->convertPrincipal($principalUri, true);
355
+        $fields = array_values($this->propertyMap);
356
+        $fields[] = 'id';
357
+        $fields[] = 'uri';
358
+        $fields[] = 'synctoken';
359
+        $fields[] = 'components';
360
+        $fields[] = 'principaluri';
361
+        $fields[] = 'transparent';
362
+        // Making fields a comma-delimited list
363
+        $query = $this->db->getQueryBuilder();
364
+        $query->select($fields)->from('calendars')
365
+            ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
366
+            ->orderBy('calendarorder', 'ASC');
367
+        $stmt = $query->execute();
368
+        $calendars = [];
369
+        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
370
+            $components = [];
371
+            if ($row['components']) {
372
+                $components = explode(',',$row['components']);
373
+            }
374
+            $calendar = [
375
+                'id' => $row['id'],
376
+                'uri' => $row['uri'],
377
+                'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
378
+                '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
379
+                '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
380
+                '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
381
+                '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
382
+            ];
383
+            foreach($this->propertyMap as $xmlName=>$dbName) {
384
+                $calendar[$xmlName] = $row[$dbName];
385
+            }
386
+
387
+            $this->addOwnerPrincipal($calendar);
388
+
389
+            if (!isset($calendars[$calendar['id']])) {
390
+                $calendars[$calendar['id']] = $calendar;
391
+            }
392
+        }
393
+        $stmt->closeCursor();
394
+        return array_values($calendars);
395
+    }
396
+
397
+
398
+    private function getUserDisplayName($uid) {
399
+        if (!isset($this->userDisplayNames[$uid])) {
400
+            $user = $this->userManager->get($uid);
401
+
402
+            if ($user instanceof IUser) {
403
+                $this->userDisplayNames[$uid] = $user->getDisplayName();
404
+            } else {
405
+                $this->userDisplayNames[$uid] = $uid;
406
+            }
407
+        }
408
+
409
+        return $this->userDisplayNames[$uid];
410
+    }
411 411
 	
412
-	/**
413
-	 * @return array
414
-	 */
415
-	public function getPublicCalendars() {
416
-		$fields = array_values($this->propertyMap);
417
-		$fields[] = 'a.id';
418
-		$fields[] = 'a.uri';
419
-		$fields[] = 'a.synctoken';
420
-		$fields[] = 'a.components';
421
-		$fields[] = 'a.principaluri';
422
-		$fields[] = 'a.transparent';
423
-		$fields[] = 's.access';
424
-		$fields[] = 's.publicuri';
425
-		$calendars = [];
426
-		$query = $this->db->getQueryBuilder();
427
-		$result = $query->select($fields)
428
-			->from('dav_shares', 's')
429
-			->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
430
-			->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
431
-			->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar')))
432
-			->execute();
433
-
434
-		while($row = $result->fetch()) {
435
-			list(, $name) = URLUtil::splitPath($row['principaluri']);
436
-			$row['displayname'] = $row['displayname'] . "($name)";
437
-			$components = [];
438
-			if ($row['components']) {
439
-				$components = explode(',',$row['components']);
440
-			}
441
-			$calendar = [
442
-				'id' => $row['id'],
443
-				'uri' => $row['publicuri'],
444
-				'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
445
-				'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
446
-				'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
447
-				'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
448
-				'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
449
-				'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], $this->legacyEndpoint),
450
-				'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
451
-				'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
452
-			];
453
-
454
-			foreach($this->propertyMap as $xmlName=>$dbName) {
455
-				$calendar[$xmlName] = $row[$dbName];
456
-			}
457
-
458
-			$this->addOwnerPrincipal($calendar);
459
-
460
-			if (!isset($calendars[$calendar['id']])) {
461
-				$calendars[$calendar['id']] = $calendar;
462
-			}
463
-		}
464
-		$result->closeCursor();
465
-
466
-		return array_values($calendars);
467
-	}
468
-
469
-	/**
470
-	 * @param string $uri
471
-	 * @return array
472
-	 * @throws NotFound
473
-	 */
474
-	public function getPublicCalendar($uri) {
475
-		$fields = array_values($this->propertyMap);
476
-		$fields[] = 'a.id';
477
-		$fields[] = 'a.uri';
478
-		$fields[] = 'a.synctoken';
479
-		$fields[] = 'a.components';
480
-		$fields[] = 'a.principaluri';
481
-		$fields[] = 'a.transparent';
482
-		$fields[] = 's.access';
483
-		$fields[] = 's.publicuri';
484
-		$query = $this->db->getQueryBuilder();
485
-		$result = $query->select($fields)
486
-			->from('dav_shares', 's')
487
-			->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
488
-			->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
489
-			->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar')))
490
-			->andWhere($query->expr()->eq('s.publicuri', $query->createNamedParameter($uri)))
491
-			->execute();
492
-
493
-		$row = $result->fetch(\PDO::FETCH_ASSOC);
494
-
495
-		$result->closeCursor();
496
-
497
-		if ($row === false) {
498
-			throw new NotFound('Node with name \'' . $uri . '\' could not be found');
499
-		}
500
-
501
-		list(, $name) = URLUtil::splitPath($row['principaluri']);
502
-		$row['displayname'] = $row['displayname'] . ' ' . "($name)";
503
-		$components = [];
504
-		if ($row['components']) {
505
-			$components = explode(',',$row['components']);
506
-		}
507
-		$calendar = [
508
-			'id' => $row['id'],
509
-			'uri' => $row['publicuri'],
510
-			'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
511
-			'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
512
-			'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
513
-			'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
514
-			'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
515
-			'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
516
-			'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
517
-			'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
518
-		];
519
-
520
-		foreach($this->propertyMap as $xmlName=>$dbName) {
521
-			$calendar[$xmlName] = $row[$dbName];
522
-		}
523
-
524
-		$this->addOwnerPrincipal($calendar);
525
-
526
-		return $calendar;
527
-
528
-	}
529
-
530
-	/**
531
-	 * @param string $principal
532
-	 * @param string $uri
533
-	 * @return array|null
534
-	 */
535
-	public function getCalendarByUri($principal, $uri) {
536
-		$fields = array_values($this->propertyMap);
537
-		$fields[] = 'id';
538
-		$fields[] = 'uri';
539
-		$fields[] = 'synctoken';
540
-		$fields[] = 'components';
541
-		$fields[] = 'principaluri';
542
-		$fields[] = 'transparent';
543
-
544
-		// Making fields a comma-delimited list
545
-		$query = $this->db->getQueryBuilder();
546
-		$query->select($fields)->from('calendars')
547
-			->where($query->expr()->eq('uri', $query->createNamedParameter($uri)))
548
-			->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal)))
549
-			->setMaxResults(1);
550
-		$stmt = $query->execute();
551
-
552
-		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
553
-		$stmt->closeCursor();
554
-		if ($row === false) {
555
-			return null;
556
-		}
557
-
558
-		$components = [];
559
-		if ($row['components']) {
560
-			$components = explode(',',$row['components']);
561
-		}
562
-
563
-		$calendar = [
564
-			'id' => $row['id'],
565
-			'uri' => $row['uri'],
566
-			'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
567
-			'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
568
-			'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
569
-			'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
570
-			'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
571
-		];
572
-
573
-		foreach($this->propertyMap as $xmlName=>$dbName) {
574
-			$calendar[$xmlName] = $row[$dbName];
575
-		}
576
-
577
-		$this->addOwnerPrincipal($calendar);
578
-
579
-		return $calendar;
580
-	}
581
-
582
-	public function getCalendarById($calendarId) {
583
-		$fields = array_values($this->propertyMap);
584
-		$fields[] = 'id';
585
-		$fields[] = 'uri';
586
-		$fields[] = 'synctoken';
587
-		$fields[] = 'components';
588
-		$fields[] = 'principaluri';
589
-		$fields[] = 'transparent';
590
-
591
-		// Making fields a comma-delimited list
592
-		$query = $this->db->getQueryBuilder();
593
-		$query->select($fields)->from('calendars')
594
-			->where($query->expr()->eq('id', $query->createNamedParameter($calendarId)))
595
-			->setMaxResults(1);
596
-		$stmt = $query->execute();
597
-
598
-		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
599
-		$stmt->closeCursor();
600
-		if ($row === false) {
601
-			return null;
602
-		}
603
-
604
-		$components = [];
605
-		if ($row['components']) {
606
-			$components = explode(',',$row['components']);
607
-		}
608
-
609
-		$calendar = [
610
-			'id' => $row['id'],
611
-			'uri' => $row['uri'],
612
-			'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
613
-			'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
614
-			'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
615
-			'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
616
-			'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
617
-		];
618
-
619
-		foreach($this->propertyMap as $xmlName=>$dbName) {
620
-			$calendar[$xmlName] = $row[$dbName];
621
-		}
622
-
623
-		$this->addOwnerPrincipal($calendar);
624
-
625
-		return $calendar;
626
-	}
627
-
628
-	/**
629
-	 * Creates a new calendar for a principal.
630
-	 *
631
-	 * If the creation was a success, an id must be returned that can be used to reference
632
-	 * this calendar in other methods, such as updateCalendar.
633
-	 *
634
-	 * @param string $principalUri
635
-	 * @param string $calendarUri
636
-	 * @param array $properties
637
-	 * @return int
638
-	 */
639
-	function createCalendar($principalUri, $calendarUri, array $properties) {
640
-		$values = [
641
-			'principaluri' => $this->convertPrincipal($principalUri, true),
642
-			'uri'          => $calendarUri,
643
-			'synctoken'    => 1,
644
-			'transparent'  => 0,
645
-			'components'   => 'VEVENT,VTODO',
646
-			'displayname'  => $calendarUri
647
-		];
648
-
649
-		// Default value
650
-		$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
651
-		if (isset($properties[$sccs])) {
652
-			if (!($properties[$sccs] instanceof SupportedCalendarComponentSet)) {
653
-				throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet');
654
-			}
655
-			$values['components'] = implode(',',$properties[$sccs]->getValue());
656
-		}
657
-		$transp = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp';
658
-		if (isset($properties[$transp])) {
659
-			$values['transparent'] = (int) ($properties[$transp]->getValue() === 'transparent');
660
-		}
661
-
662
-		foreach($this->propertyMap as $xmlName=>$dbName) {
663
-			if (isset($properties[$xmlName])) {
664
-				$values[$dbName] = $properties[$xmlName];
665
-			}
666
-		}
667
-
668
-		$query = $this->db->getQueryBuilder();
669
-		$query->insert('calendars');
670
-		foreach($values as $column => $value) {
671
-			$query->setValue($column, $query->createNamedParameter($value));
672
-		}
673
-		$query->execute();
674
-		$calendarId = $query->getLastInsertId();
675
-
676
-		$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', new GenericEvent(
677
-			'\OCA\DAV\CalDAV\CalDavBackend::createCalendar',
678
-			[
679
-				'calendarId' => $calendarId,
680
-				'calendarData' => $this->getCalendarById($calendarId),
681
-		]));
682
-
683
-		return $calendarId;
684
-	}
685
-
686
-	/**
687
-	 * Updates properties for a calendar.
688
-	 *
689
-	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
690
-	 * To do the actual updates, you must tell this object which properties
691
-	 * you're going to process with the handle() method.
692
-	 *
693
-	 * Calling the handle method is like telling the PropPatch object "I
694
-	 * promise I can handle updating this property".
695
-	 *
696
-	 * Read the PropPatch documentation for more info and examples.
697
-	 *
698
-	 * @param PropPatch $propPatch
699
-	 * @return void
700
-	 */
701
-	function updateCalendar($calendarId, PropPatch $propPatch) {
702
-		$supportedProperties = array_keys($this->propertyMap);
703
-		$supportedProperties[] = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp';
704
-
705
-		$propPatch->handle($supportedProperties, function($mutations) use ($calendarId) {
706
-			$newValues = [];
707
-			foreach ($mutations as $propertyName => $propertyValue) {
708
-
709
-				switch ($propertyName) {
710
-					case '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' :
711
-						$fieldName = 'transparent';
712
-						$newValues[$fieldName] = (int) ($propertyValue->getValue() === 'transparent');
713
-						break;
714
-					default :
715
-						$fieldName = $this->propertyMap[$propertyName];
716
-						$newValues[$fieldName] = $propertyValue;
717
-						break;
718
-				}
719
-
720
-			}
721
-			$query = $this->db->getQueryBuilder();
722
-			$query->update('calendars');
723
-			foreach ($newValues as $fieldName => $value) {
724
-				$query->set($fieldName, $query->createNamedParameter($value));
725
-			}
726
-			$query->where($query->expr()->eq('id', $query->createNamedParameter($calendarId)));
727
-			$query->execute();
728
-
729
-			$this->addChange($calendarId, "", 2);
730
-
731
-			$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', new GenericEvent(
732
-				'\OCA\DAV\CalDAV\CalDavBackend::updateCalendar',
733
-				[
734
-					'calendarId' => $calendarId,
735
-					'calendarData' => $this->getCalendarById($calendarId),
736
-					'shares' => $this->getShares($calendarId),
737
-					'propertyMutations' => $mutations,
738
-			]));
739
-
740
-			return true;
741
-		});
742
-	}
743
-
744
-	/**
745
-	 * Delete a calendar and all it's objects
746
-	 *
747
-	 * @param mixed $calendarId
748
-	 * @return void
749
-	 */
750
-	function deleteCalendar($calendarId) {
751
-		$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', new GenericEvent(
752
-			'\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar',
753
-			[
754
-				'calendarId' => $calendarId,
755
-				'calendarData' => $this->getCalendarById($calendarId),
756
-				'shares' => $this->getShares($calendarId),
757
-		]));
758
-
759
-		$stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?');
760
-		$stmt->execute([$calendarId]);
761
-
762
-		$stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendars` WHERE `id` = ?');
763
-		$stmt->execute([$calendarId]);
764
-
765
-		$stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarchanges` WHERE `calendarid` = ?');
766
-		$stmt->execute([$calendarId]);
767
-
768
-		$this->sharingBackend->deleteAllShares($calendarId);
769
-
770
-		$query = $this->db->getQueryBuilder();
771
-		$query->delete($this->dbObjectPropertiesTable)
772
-			->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
773
-			->execute();
774
-	}
775
-
776
-	/**
777
-	 * Delete all of an user's shares
778
-	 *
779
-	 * @param string $principaluri
780
-	 * @return void
781
-	 */
782
-	function deleteAllSharesByUser($principaluri) {
783
-		$this->sharingBackend->deleteAllSharesByUser($principaluri);
784
-	}
785
-
786
-	/**
787
-	 * Returns all calendar objects within a calendar.
788
-	 *
789
-	 * Every item contains an array with the following keys:
790
-	 *   * calendardata - The iCalendar-compatible calendar data
791
-	 *   * uri - a unique key which will be used to construct the uri. This can
792
-	 *     be any arbitrary string, but making sure it ends with '.ics' is a
793
-	 *     good idea. This is only the basename, or filename, not the full
794
-	 *     path.
795
-	 *   * lastmodified - a timestamp of the last modification time
796
-	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
797
-	 *   '"abcdef"')
798
-	 *   * size - The size of the calendar objects, in bytes.
799
-	 *   * component - optional, a string containing the type of object, such
800
-	 *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
801
-	 *     the Content-Type header.
802
-	 *
803
-	 * Note that the etag is optional, but it's highly encouraged to return for
804
-	 * speed reasons.
805
-	 *
806
-	 * The calendardata is also optional. If it's not returned
807
-	 * 'getCalendarObject' will be called later, which *is* expected to return
808
-	 * calendardata.
809
-	 *
810
-	 * If neither etag or size are specified, the calendardata will be
811
-	 * used/fetched to determine these numbers. If both are specified the
812
-	 * amount of times this is needed is reduced by a great degree.
813
-	 *
814
-	 * @param mixed $calendarId
815
-	 * @return array
816
-	 */
817
-	function getCalendarObjects($calendarId) {
818
-		$query = $this->db->getQueryBuilder();
819
-		$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype', 'classification'])
820
-			->from('calendarobjects')
821
-			->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
822
-		$stmt = $query->execute();
823
-
824
-		$result = [];
825
-		foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
826
-			$result[] = [
827
-					'id'           => $row['id'],
828
-					'uri'          => $row['uri'],
829
-					'lastmodified' => $row['lastmodified'],
830
-					'etag'         => '"' . $row['etag'] . '"',
831
-					'calendarid'   => $row['calendarid'],
832
-					'size'         => (int)$row['size'],
833
-					'component'    => strtolower($row['componenttype']),
834
-					'classification'=> (int)$row['classification']
835
-			];
836
-		}
837
-
838
-		return $result;
839
-	}
840
-
841
-	/**
842
-	 * Returns information from a single calendar object, based on it's object
843
-	 * uri.
844
-	 *
845
-	 * The object uri is only the basename, or filename and not a full path.
846
-	 *
847
-	 * The returned array must have the same keys as getCalendarObjects. The
848
-	 * 'calendardata' object is required here though, while it's not required
849
-	 * for getCalendarObjects.
850
-	 *
851
-	 * This method must return null if the object did not exist.
852
-	 *
853
-	 * @param mixed $calendarId
854
-	 * @param string $objectUri
855
-	 * @return array|null
856
-	 */
857
-	function getCalendarObject($calendarId, $objectUri) {
858
-
859
-		$query = $this->db->getQueryBuilder();
860
-		$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
861
-				->from('calendarobjects')
862
-				->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
863
-				->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)));
864
-		$stmt = $query->execute();
865
-		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
866
-
867
-		if(!$row) return null;
868
-
869
-		return [
870
-				'id'            => $row['id'],
871
-				'uri'           => $row['uri'],
872
-				'lastmodified'  => $row['lastmodified'],
873
-				'etag'          => '"' . $row['etag'] . '"',
874
-				'calendarid'    => $row['calendarid'],
875
-				'size'          => (int)$row['size'],
876
-				'calendardata'  => $this->readBlob($row['calendardata']),
877
-				'component'     => strtolower($row['componenttype']),
878
-				'classification'=> (int)$row['classification']
879
-		];
880
-	}
881
-
882
-	/**
883
-	 * Returns a list of calendar objects.
884
-	 *
885
-	 * This method should work identical to getCalendarObject, but instead
886
-	 * return all the calendar objects in the list as an array.
887
-	 *
888
-	 * If the backend supports this, it may allow for some speed-ups.
889
-	 *
890
-	 * @param mixed $calendarId
891
-	 * @param string[] $uris
892
-	 * @return array
893
-	 */
894
-	function getMultipleCalendarObjects($calendarId, array $uris) {
895
-		if (empty($uris)) {
896
-			return [];
897
-		}
898
-
899
-		$chunks = array_chunk($uris, 100);
900
-		$objects = [];
901
-
902
-		$query = $this->db->getQueryBuilder();
903
-		$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
904
-			->from('calendarobjects')
905
-			->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
906
-			->andWhere($query->expr()->in('uri', $query->createParameter('uri')));
907
-
908
-		foreach ($chunks as $uris) {
909
-			$query->setParameter('uri', $uris, IQueryBuilder::PARAM_STR_ARRAY);
910
-			$result = $query->execute();
911
-
912
-			while ($row = $result->fetch()) {
913
-				$objects[] = [
914
-					'id'           => $row['id'],
915
-					'uri'          => $row['uri'],
916
-					'lastmodified' => $row['lastmodified'],
917
-					'etag'         => '"' . $row['etag'] . '"',
918
-					'calendarid'   => $row['calendarid'],
919
-					'size'         => (int)$row['size'],
920
-					'calendardata' => $this->readBlob($row['calendardata']),
921
-					'component'    => strtolower($row['componenttype']),
922
-					'classification' => (int)$row['classification']
923
-				];
924
-			}
925
-			$result->closeCursor();
926
-		}
927
-		return $objects;
928
-	}
929
-
930
-	/**
931
-	 * Creates a new calendar object.
932
-	 *
933
-	 * The object uri is only the basename, or filename and not a full path.
934
-	 *
935
-	 * It is possible return an etag from this function, which will be used in
936
-	 * the response to this PUT request. Note that the ETag must be surrounded
937
-	 * by double-quotes.
938
-	 *
939
-	 * However, you should only really return this ETag if you don't mangle the
940
-	 * calendar-data. If the result of a subsequent GET to this object is not
941
-	 * the exact same as this request body, you should omit the ETag.
942
-	 *
943
-	 * @param mixed $calendarId
944
-	 * @param string $objectUri
945
-	 * @param string $calendarData
946
-	 * @return string
947
-	 */
948
-	function createCalendarObject($calendarId, $objectUri, $calendarData) {
949
-		$extraData = $this->getDenormalizedData($calendarData);
950
-
951
-		$query = $this->db->getQueryBuilder();
952
-		$query->insert('calendarobjects')
953
-			->values([
954
-				'calendarid' => $query->createNamedParameter($calendarId),
955
-				'uri' => $query->createNamedParameter($objectUri),
956
-				'calendardata' => $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB),
957
-				'lastmodified' => $query->createNamedParameter(time()),
958
-				'etag' => $query->createNamedParameter($extraData['etag']),
959
-				'size' => $query->createNamedParameter($extraData['size']),
960
-				'componenttype' => $query->createNamedParameter($extraData['componentType']),
961
-				'firstoccurence' => $query->createNamedParameter($extraData['firstOccurence']),
962
-				'lastoccurence' => $query->createNamedParameter($extraData['lastOccurence']),
963
-				'classification' => $query->createNamedParameter($extraData['classification']),
964
-				'uid' => $query->createNamedParameter($extraData['uid']),
965
-			])
966
-			->execute();
967
-
968
-		$this->updateProperties($calendarId, $objectUri, $calendarData);
969
-
970
-		$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', new GenericEvent(
971
-			'\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
972
-			[
973
-				'calendarId' => $calendarId,
974
-				'calendarData' => $this->getCalendarById($calendarId),
975
-				'shares' => $this->getShares($calendarId),
976
-				'objectData' => $this->getCalendarObject($calendarId, $objectUri),
977
-			]
978
-		));
979
-		$this->addChange($calendarId, $objectUri, 1);
980
-
981
-		return '"' . $extraData['etag'] . '"';
982
-	}
983
-
984
-	/**
985
-	 * Updates an existing calendarobject, based on it's uri.
986
-	 *
987
-	 * The object uri is only the basename, or filename and not a full path.
988
-	 *
989
-	 * It is possible return an etag from this function, which will be used in
990
-	 * the response to this PUT request. Note that the ETag must be surrounded
991
-	 * by double-quotes.
992
-	 *
993
-	 * However, you should only really return this ETag if you don't mangle the
994
-	 * calendar-data. If the result of a subsequent GET to this object is not
995
-	 * the exact same as this request body, you should omit the ETag.
996
-	 *
997
-	 * @param mixed $calendarId
998
-	 * @param string $objectUri
999
-	 * @param string $calendarData
1000
-	 * @return string
1001
-	 */
1002
-	function updateCalendarObject($calendarId, $objectUri, $calendarData) {
1003
-		$extraData = $this->getDenormalizedData($calendarData);
1004
-
1005
-		$query = $this->db->getQueryBuilder();
1006
-		$query->update('calendarobjects')
1007
-				->set('calendardata', $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB))
1008
-				->set('lastmodified', $query->createNamedParameter(time()))
1009
-				->set('etag', $query->createNamedParameter($extraData['etag']))
1010
-				->set('size', $query->createNamedParameter($extraData['size']))
1011
-				->set('componenttype', $query->createNamedParameter($extraData['componentType']))
1012
-				->set('firstoccurence', $query->createNamedParameter($extraData['firstOccurence']))
1013
-				->set('lastoccurence', $query->createNamedParameter($extraData['lastOccurence']))
1014
-				->set('classification', $query->createNamedParameter($extraData['classification']))
1015
-				->set('uid', $query->createNamedParameter($extraData['uid']))
1016
-			->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
1017
-			->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1018
-			->execute();
1019
-
1020
-		$this->updateProperties($calendarId, $objectUri, $calendarData);
1021
-
1022
-		$data = $this->getCalendarObject($calendarId, $objectUri);
1023
-		if (is_array($data)) {
1024
-			$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', new GenericEvent(
1025
-				'\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject',
1026
-				[
1027
-					'calendarId' => $calendarId,
1028
-					'calendarData' => $this->getCalendarById($calendarId),
1029
-					'shares' => $this->getShares($calendarId),
1030
-					'objectData' => $data,
1031
-				]
1032
-			));
1033
-		}
1034
-		$this->addChange($calendarId, $objectUri, 2);
1035
-
1036
-		return '"' . $extraData['etag'] . '"';
1037
-	}
1038
-
1039
-	/**
1040
-	 * @param int $calendarObjectId
1041
-	 * @param int $classification
1042
-	 */
1043
-	public function setClassification($calendarObjectId, $classification) {
1044
-		if (!in_array($classification, [
1045
-			self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL
1046
-		])) {
1047
-			throw new \InvalidArgumentException();
1048
-		}
1049
-		$query = $this->db->getQueryBuilder();
1050
-		$query->update('calendarobjects')
1051
-			->set('classification', $query->createNamedParameter($classification))
1052
-			->where($query->expr()->eq('id', $query->createNamedParameter($calendarObjectId)))
1053
-			->execute();
1054
-	}
1055
-
1056
-	/**
1057
-	 * Deletes an existing calendar object.
1058
-	 *
1059
-	 * The object uri is only the basename, or filename and not a full path.
1060
-	 *
1061
-	 * @param mixed $calendarId
1062
-	 * @param string $objectUri
1063
-	 * @return void
1064
-	 */
1065
-	function deleteCalendarObject($calendarId, $objectUri) {
1066
-		$data = $this->getCalendarObject($calendarId, $objectUri);
1067
-		if (is_array($data)) {
1068
-			$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', new GenericEvent(
1069
-				'\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject',
1070
-				[
1071
-					'calendarId' => $calendarId,
1072
-					'calendarData' => $this->getCalendarById($calendarId),
1073
-					'shares' => $this->getShares($calendarId),
1074
-					'objectData' => $data,
1075
-				]
1076
-			));
1077
-		}
1078
-
1079
-		$stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `uri` = ?');
1080
-		$stmt->execute([$calendarId, $objectUri]);
1081
-
1082
-		$this->purgeProperties($calendarId, $data['id']);
1083
-
1084
-		$this->addChange($calendarId, $objectUri, 3);
1085
-	}
1086
-
1087
-	/**
1088
-	 * Performs a calendar-query on the contents of this calendar.
1089
-	 *
1090
-	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
1091
-	 * calendar-query it is possible for a client to request a specific set of
1092
-	 * object, based on contents of iCalendar properties, date-ranges and
1093
-	 * iCalendar component types (VTODO, VEVENT).
1094
-	 *
1095
-	 * This method should just return a list of (relative) urls that match this
1096
-	 * query.
1097
-	 *
1098
-	 * The list of filters are specified as an array. The exact array is
1099
-	 * documented by Sabre\CalDAV\CalendarQueryParser.
1100
-	 *
1101
-	 * Note that it is extremely likely that getCalendarObject for every path
1102
-	 * returned from this method will be called almost immediately after. You
1103
-	 * may want to anticipate this to speed up these requests.
1104
-	 *
1105
-	 * This method provides a default implementation, which parses *all* the
1106
-	 * iCalendar objects in the specified calendar.
1107
-	 *
1108
-	 * This default may well be good enough for personal use, and calendars
1109
-	 * that aren't very large. But if you anticipate high usage, big calendars
1110
-	 * or high loads, you are strongly advised to optimize certain paths.
1111
-	 *
1112
-	 * The best way to do so is override this method and to optimize
1113
-	 * specifically for 'common filters'.
1114
-	 *
1115
-	 * Requests that are extremely common are:
1116
-	 *   * requests for just VEVENTS
1117
-	 *   * requests for just VTODO
1118
-	 *   * requests with a time-range-filter on either VEVENT or VTODO.
1119
-	 *
1120
-	 * ..and combinations of these requests. It may not be worth it to try to
1121
-	 * handle every possible situation and just rely on the (relatively
1122
-	 * easy to use) CalendarQueryValidator to handle the rest.
1123
-	 *
1124
-	 * Note that especially time-range-filters may be difficult to parse. A
1125
-	 * time-range filter specified on a VEVENT must for instance also handle
1126
-	 * recurrence rules correctly.
1127
-	 * A good example of how to interprete all these filters can also simply
1128
-	 * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
1129
-	 * as possible, so it gives you a good idea on what type of stuff you need
1130
-	 * to think of.
1131
-	 *
1132
-	 * @param mixed $calendarId
1133
-	 * @param array $filters
1134
-	 * @return array
1135
-	 */
1136
-	function calendarQuery($calendarId, array $filters) {
1137
-		$componentType = null;
1138
-		$requirePostFilter = true;
1139
-		$timeRange = null;
1140
-
1141
-		// if no filters were specified, we don't need to filter after a query
1142
-		if (!$filters['prop-filters'] && !$filters['comp-filters']) {
1143
-			$requirePostFilter = false;
1144
-		}
1145
-
1146
-		// Figuring out if there's a component filter
1147
-		if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
1148
-			$componentType = $filters['comp-filters'][0]['name'];
1149
-
1150
-			// Checking if we need post-filters
1151
-			if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
1152
-				$requirePostFilter = false;
1153
-			}
1154
-			// There was a time-range filter
1155
-			if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
1156
-				$timeRange = $filters['comp-filters'][0]['time-range'];
1157
-
1158
-				// If start time OR the end time is not specified, we can do a
1159
-				// 100% accurate mysql query.
1160
-				if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
1161
-					$requirePostFilter = false;
1162
-				}
1163
-			}
1164
-
1165
-		}
1166
-		$columns = ['uri'];
1167
-		if ($requirePostFilter) {
1168
-			$columns = ['uri', 'calendardata'];
1169
-		}
1170
-		$query = $this->db->getQueryBuilder();
1171
-		$query->select($columns)
1172
-			->from('calendarobjects')
1173
-			->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
1174
-
1175
-		if ($componentType) {
1176
-			$query->andWhere($query->expr()->eq('componenttype', $query->createNamedParameter($componentType)));
1177
-		}
1178
-
1179
-		if ($timeRange && $timeRange['start']) {
1180
-			$query->andWhere($query->expr()->gt('lastoccurence', $query->createNamedParameter($timeRange['start']->getTimeStamp())));
1181
-		}
1182
-		if ($timeRange && $timeRange['end']) {
1183
-			$query->andWhere($query->expr()->lt('firstoccurence', $query->createNamedParameter($timeRange['end']->getTimeStamp())));
1184
-		}
1185
-
1186
-		$stmt = $query->execute();
1187
-
1188
-		$result = [];
1189
-		while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1190
-			if ($requirePostFilter) {
1191
-				if (!$this->validateFilterForObject($row, $filters)) {
1192
-					continue;
1193
-				}
1194
-			}
1195
-			$result[] = $row['uri'];
1196
-		}
1197
-
1198
-		return $result;
1199
-	}
1200
-
1201
-	/**
1202
-	 * custom Nextcloud search extension for CalDAV
1203
-	 *
1204
-	 * @param string $principalUri
1205
-	 * @param array $filters
1206
-	 * @param integer|null $limit
1207
-	 * @param integer|null $offset
1208
-	 * @return array
1209
-	 */
1210
-	public function calendarSearch($principalUri, array $filters, $limit=null, $offset=null) {
1211
-		$calendars = $this->getCalendarsForUser($principalUri);
1212
-		$ownCalendars = [];
1213
-		$sharedCalendars = [];
1214
-
1215
-		$uriMapper = [];
1216
-
1217
-		foreach($calendars as $calendar) {
1218
-			if ($calendar['{http://owncloud.org/ns}owner-principal'] === $principalUri) {
1219
-				$ownCalendars[] = $calendar['id'];
1220
-			} else {
1221
-				$sharedCalendars[] = $calendar['id'];
1222
-			}
1223
-			$uriMapper[$calendar['id']] = $calendar['uri'];
1224
-		}
1225
-		if (count($ownCalendars) === 0 && count($sharedCalendars) === 0) {
1226
-			return [];
1227
-		}
1228
-
1229
-		$query = $this->db->getQueryBuilder();
1230
-		// Calendar id expressions
1231
-		$calendarExpressions = [];
1232
-		foreach($ownCalendars as $id) {
1233
-			$calendarExpressions[] = $query->expr()
1234
-				->eq('c.calendarid', $query->createNamedParameter($id));
1235
-		}
1236
-		foreach($sharedCalendars as $id) {
1237
-			$calendarExpressions[] = $query->expr()->andX(
1238
-				$query->expr()->eq('c.calendarid',
1239
-					$query->createNamedParameter($id)),
1240
-				$query->expr()->eq('c.classification',
1241
-					$query->createNamedParameter(self::CLASSIFICATION_PUBLIC))
1242
-			);
1243
-		}
1244
-
1245
-		if (count($calendarExpressions) === 1) {
1246
-			$calExpr = $calendarExpressions[0];
1247
-		} else {
1248
-			$calExpr = call_user_func_array([$query->expr(), 'orX'], $calendarExpressions);
1249
-		}
1250
-
1251
-		// Component expressions
1252
-		$compExpressions = [];
1253
-		foreach($filters['comps'] as $comp) {
1254
-			$compExpressions[] = $query->expr()
1255
-				->eq('c.componenttype', $query->createNamedParameter($comp));
1256
-		}
1257
-
1258
-		if (count($compExpressions) === 1) {
1259
-			$compExpr = $compExpressions[0];
1260
-		} else {
1261
-			$compExpr = call_user_func_array([$query->expr(), 'orX'], $compExpressions);
1262
-		}
1263
-
1264
-		if (!isset($filters['props'])) {
1265
-			$filters['props'] = [];
1266
-		}
1267
-		if (!isset($filters['params'])) {
1268
-			$filters['params'] = [];
1269
-		}
1270
-
1271
-		$propParamExpressions = [];
1272
-		foreach($filters['props'] as $prop) {
1273
-			$propParamExpressions[] = $query->expr()->andX(
1274
-				$query->expr()->eq('i.name', $query->createNamedParameter($prop)),
1275
-				$query->expr()->isNull('i.parameter')
1276
-			);
1277
-		}
1278
-		foreach($filters['params'] as $param) {
1279
-			$propParamExpressions[] = $query->expr()->andX(
1280
-				$query->expr()->eq('i.name', $query->createNamedParameter($param['property'])),
1281
-				$query->expr()->eq('i.parameter', $query->createNamedParameter($param['parameter']))
1282
-			);
1283
-		}
1284
-
1285
-		if (count($propParamExpressions) === 1) {
1286
-			$propParamExpr = $propParamExpressions[0];
1287
-		} else {
1288
-			$propParamExpr = call_user_func_array([$query->expr(), 'orX'], $propParamExpressions);
1289
-		}
1290
-
1291
-		$query->select(['c.calendarid', 'c.uri'])
1292
-			->from($this->dbObjectPropertiesTable, 'i')
1293
-			->join('i', 'calendarobjects', 'c', $query->expr()->eq('i.objectid', 'c.id'))
1294
-			->where($calExpr)
1295
-			->andWhere($compExpr)
1296
-			->andWhere($propParamExpr)
1297
-			->andWhere($query->expr()->iLike('i.value',
1298
-				$query->createNamedParameter('%'.$this->db->escapeLikeParameter($filters['search-term']).'%')));
1299
-
1300
-		if ($offset) {
1301
-			$query->setFirstResult($offset);
1302
-		}
1303
-		if ($limit) {
1304
-			$query->setMaxResults($limit);
1305
-		}
1306
-
1307
-		$stmt = $query->execute();
1308
-
1309
-		$result = [];
1310
-		while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1311
-			$path = $uriMapper[$row['calendarid']] . '/' . $row['uri'];
1312
-			if (!in_array($path, $result)) {
1313
-				$result[] = $path;
1314
-			}
1315
-		}
1316
-
1317
-		return $result;
1318
-	}
1319
-
1320
-	/**
1321
-	 * Searches through all of a users calendars and calendar objects to find
1322
-	 * an object with a specific UID.
1323
-	 *
1324
-	 * This method should return the path to this object, relative to the
1325
-	 * calendar home, so this path usually only contains two parts:
1326
-	 *
1327
-	 * calendarpath/objectpath.ics
1328
-	 *
1329
-	 * If the uid is not found, return null.
1330
-	 *
1331
-	 * This method should only consider * objects that the principal owns, so
1332
-	 * any calendars owned by other principals that also appear in this
1333
-	 * collection should be ignored.
1334
-	 *
1335
-	 * @param string $principalUri
1336
-	 * @param string $uid
1337
-	 * @return string|null
1338
-	 */
1339
-	function getCalendarObjectByUID($principalUri, $uid) {
1340
-
1341
-		$query = $this->db->getQueryBuilder();
1342
-		$query->selectAlias('c.uri', 'calendaruri')->selectAlias('co.uri', 'objecturi')
1343
-			->from('calendarobjects', 'co')
1344
-			->leftJoin('co', 'calendars', 'c', $query->expr()->eq('co.calendarid', 'c.id'))
1345
-			->where($query->expr()->eq('c.principaluri', $query->createNamedParameter($principalUri)))
1346
-			->andWhere($query->expr()->eq('co.uid', $query->createNamedParameter($uid)));
1347
-
1348
-		$stmt = $query->execute();
1349
-
1350
-		if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1351
-			return $row['calendaruri'] . '/' . $row['objecturi'];
1352
-		}
1353
-
1354
-		return null;
1355
-	}
1356
-
1357
-	/**
1358
-	 * The getChanges method returns all the changes that have happened, since
1359
-	 * the specified syncToken in the specified calendar.
1360
-	 *
1361
-	 * This function should return an array, such as the following:
1362
-	 *
1363
-	 * [
1364
-	 *   'syncToken' => 'The current synctoken',
1365
-	 *   'added'   => [
1366
-	 *      'new.txt',
1367
-	 *   ],
1368
-	 *   'modified'   => [
1369
-	 *      'modified.txt',
1370
-	 *   ],
1371
-	 *   'deleted' => [
1372
-	 *      'foo.php.bak',
1373
-	 *      'old.txt'
1374
-	 *   ]
1375
-	 * );
1376
-	 *
1377
-	 * The returned syncToken property should reflect the *current* syncToken
1378
-	 * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
1379
-	 * property This is * needed here too, to ensure the operation is atomic.
1380
-	 *
1381
-	 * If the $syncToken argument is specified as null, this is an initial
1382
-	 * sync, and all members should be reported.
1383
-	 *
1384
-	 * The modified property is an array of nodenames that have changed since
1385
-	 * the last token.
1386
-	 *
1387
-	 * The deleted property is an array with nodenames, that have been deleted
1388
-	 * from collection.
1389
-	 *
1390
-	 * The $syncLevel argument is basically the 'depth' of the report. If it's
1391
-	 * 1, you only have to report changes that happened only directly in
1392
-	 * immediate descendants. If it's 2, it should also include changes from
1393
-	 * the nodes below the child collections. (grandchildren)
1394
-	 *
1395
-	 * The $limit argument allows a client to specify how many results should
1396
-	 * be returned at most. If the limit is not specified, it should be treated
1397
-	 * as infinite.
1398
-	 *
1399
-	 * If the limit (infinite or not) is higher than you're willing to return,
1400
-	 * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
1401
-	 *
1402
-	 * If the syncToken is expired (due to data cleanup) or unknown, you must
1403
-	 * return null.
1404
-	 *
1405
-	 * The limit is 'suggestive'. You are free to ignore it.
1406
-	 *
1407
-	 * @param string $calendarId
1408
-	 * @param string $syncToken
1409
-	 * @param int $syncLevel
1410
-	 * @param int $limit
1411
-	 * @return array
1412
-	 */
1413
-	function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) {
1414
-		// Current synctoken
1415
-		$stmt = $this->db->prepare('SELECT `synctoken` FROM `*PREFIX*calendars` WHERE `id` = ?');
1416
-		$stmt->execute([ $calendarId ]);
1417
-		$currentToken = $stmt->fetchColumn(0);
1418
-
1419
-		if (is_null($currentToken)) {
1420
-			return null;
1421
-		}
1422
-
1423
-		$result = [
1424
-			'syncToken' => $currentToken,
1425
-			'added'     => [],
1426
-			'modified'  => [],
1427
-			'deleted'   => [],
1428
-		];
1429
-
1430
-		if ($syncToken) {
1431
-
1432
-			$query = "SELECT `uri`, `operation` FROM `*PREFIX*calendarchanges` WHERE `synctoken` >= ? AND `synctoken` < ? AND `calendarid` = ? ORDER BY `synctoken`";
1433
-			if ($limit>0) {
1434
-				$query.= " `LIMIT` " . (int)$limit;
1435
-			}
1436
-
1437
-			// Fetching all changes
1438
-			$stmt = $this->db->prepare($query);
1439
-			$stmt->execute([$syncToken, $currentToken, $calendarId]);
1440
-
1441
-			$changes = [];
1442
-
1443
-			// This loop ensures that any duplicates are overwritten, only the
1444
-			// last change on a node is relevant.
1445
-			while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1446
-
1447
-				$changes[$row['uri']] = $row['operation'];
1448
-
1449
-			}
1450
-
1451
-			foreach($changes as $uri => $operation) {
1452
-
1453
-				switch($operation) {
1454
-					case 1 :
1455
-						$result['added'][] = $uri;
1456
-						break;
1457
-					case 2 :
1458
-						$result['modified'][] = $uri;
1459
-						break;
1460
-					case 3 :
1461
-						$result['deleted'][] = $uri;
1462
-						break;
1463
-				}
1464
-
1465
-			}
1466
-		} else {
1467
-			// No synctoken supplied, this is the initial sync.
1468
-			$query = "SELECT `uri` FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?";
1469
-			$stmt = $this->db->prepare($query);
1470
-			$stmt->execute([$calendarId]);
1471
-
1472
-			$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
1473
-		}
1474
-		return $result;
1475
-
1476
-	}
1477
-
1478
-	/**
1479
-	 * Returns a list of subscriptions for a principal.
1480
-	 *
1481
-	 * Every subscription is an array with the following keys:
1482
-	 *  * id, a unique id that will be used by other functions to modify the
1483
-	 *    subscription. This can be the same as the uri or a database key.
1484
-	 *  * uri. This is just the 'base uri' or 'filename' of the subscription.
1485
-	 *  * principaluri. The owner of the subscription. Almost always the same as
1486
-	 *    principalUri passed to this method.
1487
-	 *
1488
-	 * Furthermore, all the subscription info must be returned too:
1489
-	 *
1490
-	 * 1. {DAV:}displayname
1491
-	 * 2. {http://apple.com/ns/ical/}refreshrate
1492
-	 * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
1493
-	 *    should not be stripped).
1494
-	 * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
1495
-	 *    should not be stripped).
1496
-	 * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
1497
-	 *    attachments should not be stripped).
1498
-	 * 6. {http://calendarserver.org/ns/}source (Must be a
1499
-	 *     Sabre\DAV\Property\Href).
1500
-	 * 7. {http://apple.com/ns/ical/}calendar-color
1501
-	 * 8. {http://apple.com/ns/ical/}calendar-order
1502
-	 * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
1503
-	 *    (should just be an instance of
1504
-	 *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
1505
-	 *    default components).
1506
-	 *
1507
-	 * @param string $principalUri
1508
-	 * @return array
1509
-	 */
1510
-	function getSubscriptionsForUser($principalUri) {
1511
-		$fields = array_values($this->subscriptionPropertyMap);
1512
-		$fields[] = 'id';
1513
-		$fields[] = 'uri';
1514
-		$fields[] = 'source';
1515
-		$fields[] = 'principaluri';
1516
-		$fields[] = 'lastmodified';
1517
-
1518
-		$query = $this->db->getQueryBuilder();
1519
-		$query->select($fields)
1520
-			->from('calendarsubscriptions')
1521
-			->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1522
-			->orderBy('calendarorder', 'asc');
1523
-		$stmt =$query->execute();
1524
-
1525
-		$subscriptions = [];
1526
-		while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1527
-
1528
-			$subscription = [
1529
-				'id'           => $row['id'],
1530
-				'uri'          => $row['uri'],
1531
-				'principaluri' => $row['principaluri'],
1532
-				'source'       => $row['source'],
1533
-				'lastmodified' => $row['lastmodified'],
1534
-
1535
-				'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1536
-			];
1537
-
1538
-			foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
1539
-				if (!is_null($row[$dbName])) {
1540
-					$subscription[$xmlName] = $row[$dbName];
1541
-				}
1542
-			}
1543
-
1544
-			$subscriptions[] = $subscription;
1545
-
1546
-		}
1547
-
1548
-		return $subscriptions;
1549
-	}
1550
-
1551
-	/**
1552
-	 * Creates a new subscription for a principal.
1553
-	 *
1554
-	 * If the creation was a success, an id must be returned that can be used to reference
1555
-	 * this subscription in other methods, such as updateSubscription.
1556
-	 *
1557
-	 * @param string $principalUri
1558
-	 * @param string $uri
1559
-	 * @param array $properties
1560
-	 * @return mixed
1561
-	 */
1562
-	function createSubscription($principalUri, $uri, array $properties) {
1563
-
1564
-		if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1565
-			throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1566
-		}
1567
-
1568
-		$values = [
1569
-			'principaluri' => $principalUri,
1570
-			'uri'          => $uri,
1571
-			'source'       => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1572
-			'lastmodified' => time(),
1573
-		];
1574
-
1575
-		$propertiesBoolean = ['striptodos', 'stripalarms', 'stripattachments'];
1576
-
1577
-		foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
1578
-			if (array_key_exists($xmlName, $properties)) {
1579
-					$values[$dbName] = $properties[$xmlName];
1580
-					if (in_array($dbName, $propertiesBoolean)) {
1581
-						$values[$dbName] = true;
1582
-				}
1583
-			}
1584
-		}
1585
-
1586
-		$valuesToInsert = array();
1587
-
1588
-		$query = $this->db->getQueryBuilder();
1589
-
1590
-		foreach (array_keys($values) as $name) {
1591
-			$valuesToInsert[$name] = $query->createNamedParameter($values[$name]);
1592
-		}
1593
-
1594
-		$query->insert('calendarsubscriptions')
1595
-			->values($valuesToInsert)
1596
-			->execute();
1597
-
1598
-		return $this->db->lastInsertId('*PREFIX*calendarsubscriptions');
1599
-	}
1600
-
1601
-	/**
1602
-	 * Updates a subscription
1603
-	 *
1604
-	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1605
-	 * To do the actual updates, you must tell this object which properties
1606
-	 * you're going to process with the handle() method.
1607
-	 *
1608
-	 * Calling the handle method is like telling the PropPatch object "I
1609
-	 * promise I can handle updating this property".
1610
-	 *
1611
-	 * Read the PropPatch documentation for more info and examples.
1612
-	 *
1613
-	 * @param mixed $subscriptionId
1614
-	 * @param PropPatch $propPatch
1615
-	 * @return void
1616
-	 */
1617
-	function updateSubscription($subscriptionId, PropPatch $propPatch) {
1618
-		$supportedProperties = array_keys($this->subscriptionPropertyMap);
1619
-		$supportedProperties[] = '{http://calendarserver.org/ns/}source';
1620
-
1621
-		$propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
1622
-
1623
-			$newValues = [];
1624
-
1625
-			foreach($mutations as $propertyName=>$propertyValue) {
1626
-				if ($propertyName === '{http://calendarserver.org/ns/}source') {
1627
-					$newValues['source'] = $propertyValue->getHref();
1628
-				} else {
1629
-					$fieldName = $this->subscriptionPropertyMap[$propertyName];
1630
-					$newValues[$fieldName] = $propertyValue;
1631
-				}
1632
-			}
1633
-
1634
-			$query = $this->db->getQueryBuilder();
1635
-			$query->update('calendarsubscriptions')
1636
-				->set('lastmodified', $query->createNamedParameter(time()));
1637
-			foreach($newValues as $fieldName=>$value) {
1638
-				$query->set($fieldName, $query->createNamedParameter($value));
1639
-			}
1640
-			$query->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId)))
1641
-				->execute();
1642
-
1643
-			return true;
1644
-
1645
-		});
1646
-	}
1647
-
1648
-	/**
1649
-	 * Deletes a subscription.
1650
-	 *
1651
-	 * @param mixed $subscriptionId
1652
-	 * @return void
1653
-	 */
1654
-	function deleteSubscription($subscriptionId) {
1655
-		$query = $this->db->getQueryBuilder();
1656
-		$query->delete('calendarsubscriptions')
1657
-			->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId)))
1658
-			->execute();
1659
-	}
1660
-
1661
-	/**
1662
-	 * Returns a single scheduling object for the inbox collection.
1663
-	 *
1664
-	 * The returned array should contain the following elements:
1665
-	 *   * uri - A unique basename for the object. This will be used to
1666
-	 *           construct a full uri.
1667
-	 *   * calendardata - The iCalendar object
1668
-	 *   * lastmodified - The last modification date. Can be an int for a unix
1669
-	 *                    timestamp, or a PHP DateTime object.
1670
-	 *   * etag - A unique token that must change if the object changed.
1671
-	 *   * size - The size of the object, in bytes.
1672
-	 *
1673
-	 * @param string $principalUri
1674
-	 * @param string $objectUri
1675
-	 * @return array
1676
-	 */
1677
-	function getSchedulingObject($principalUri, $objectUri) {
1678
-		$query = $this->db->getQueryBuilder();
1679
-		$stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size'])
1680
-			->from('schedulingobjects')
1681
-			->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1682
-			->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1683
-			->execute();
1684
-
1685
-		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
1686
-
1687
-		if(!$row) {
1688
-			return null;
1689
-		}
1690
-
1691
-		return [
1692
-				'uri'          => $row['uri'],
1693
-				'calendardata' => $row['calendardata'],
1694
-				'lastmodified' => $row['lastmodified'],
1695
-				'etag'         => '"' . $row['etag'] . '"',
1696
-				'size'         => (int)$row['size'],
1697
-		];
1698
-	}
1699
-
1700
-	/**
1701
-	 * Returns all scheduling objects for the inbox collection.
1702
-	 *
1703
-	 * These objects should be returned as an array. Every item in the array
1704
-	 * should follow the same structure as returned from getSchedulingObject.
1705
-	 *
1706
-	 * The main difference is that 'calendardata' is optional.
1707
-	 *
1708
-	 * @param string $principalUri
1709
-	 * @return array
1710
-	 */
1711
-	function getSchedulingObjects($principalUri) {
1712
-		$query = $this->db->getQueryBuilder();
1713
-		$stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size'])
1714
-				->from('schedulingobjects')
1715
-				->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1716
-				->execute();
1717
-
1718
-		$result = [];
1719
-		foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1720
-			$result[] = [
1721
-					'calendardata' => $row['calendardata'],
1722
-					'uri'          => $row['uri'],
1723
-					'lastmodified' => $row['lastmodified'],
1724
-					'etag'         => '"' . $row['etag'] . '"',
1725
-					'size'         => (int)$row['size'],
1726
-			];
1727
-		}
1728
-
1729
-		return $result;
1730
-	}
1731
-
1732
-	/**
1733
-	 * Deletes a scheduling object from the inbox collection.
1734
-	 *
1735
-	 * @param string $principalUri
1736
-	 * @param string $objectUri
1737
-	 * @return void
1738
-	 */
1739
-	function deleteSchedulingObject($principalUri, $objectUri) {
1740
-		$query = $this->db->getQueryBuilder();
1741
-		$query->delete('schedulingobjects')
1742
-				->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1743
-				->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1744
-				->execute();
1745
-	}
1746
-
1747
-	/**
1748
-	 * Creates a new scheduling object. This should land in a users' inbox.
1749
-	 *
1750
-	 * @param string $principalUri
1751
-	 * @param string $objectUri
1752
-	 * @param string $objectData
1753
-	 * @return void
1754
-	 */
1755
-	function createSchedulingObject($principalUri, $objectUri, $objectData) {
1756
-		$query = $this->db->getQueryBuilder();
1757
-		$query->insert('schedulingobjects')
1758
-			->values([
1759
-				'principaluri' => $query->createNamedParameter($principalUri),
1760
-				'calendardata' => $query->createNamedParameter($objectData),
1761
-				'uri' => $query->createNamedParameter($objectUri),
1762
-				'lastmodified' => $query->createNamedParameter(time()),
1763
-				'etag' => $query->createNamedParameter(md5($objectData)),
1764
-				'size' => $query->createNamedParameter(strlen($objectData))
1765
-			])
1766
-			->execute();
1767
-	}
1768
-
1769
-	/**
1770
-	 * Adds a change record to the calendarchanges table.
1771
-	 *
1772
-	 * @param mixed $calendarId
1773
-	 * @param string $objectUri
1774
-	 * @param int $operation 1 = add, 2 = modify, 3 = delete.
1775
-	 * @return void
1776
-	 */
1777
-	protected function addChange($calendarId, $objectUri, $operation) {
1778
-
1779
-		$stmt = $this->db->prepare('INSERT INTO `*PREFIX*calendarchanges` (`uri`, `synctoken`, `calendarid`, `operation`) SELECT ?, `synctoken`, ?, ? FROM `*PREFIX*calendars` WHERE `id` = ?');
1780
-		$stmt->execute([
1781
-			$objectUri,
1782
-			$calendarId,
1783
-			$operation,
1784
-			$calendarId
1785
-		]);
1786
-		$stmt = $this->db->prepare('UPDATE `*PREFIX*calendars` SET `synctoken` = `synctoken` + 1 WHERE `id` = ?');
1787
-		$stmt->execute([
1788
-			$calendarId
1789
-		]);
1790
-
1791
-	}
1792
-
1793
-	/**
1794
-	 * Parses some information from calendar objects, used for optimized
1795
-	 * calendar-queries.
1796
-	 *
1797
-	 * Returns an array with the following keys:
1798
-	 *   * etag - An md5 checksum of the object without the quotes.
1799
-	 *   * size - Size of the object in bytes
1800
-	 *   * componentType - VEVENT, VTODO or VJOURNAL
1801
-	 *   * firstOccurence
1802
-	 *   * lastOccurence
1803
-	 *   * uid - value of the UID property
1804
-	 *
1805
-	 * @param string $calendarData
1806
-	 * @return array
1807
-	 */
1808
-	public function getDenormalizedData($calendarData) {
1809
-
1810
-		$vObject = Reader::read($calendarData);
1811
-		$componentType = null;
1812
-		$component = null;
1813
-		$firstOccurrence = null;
1814
-		$lastOccurrence = null;
1815
-		$uid = null;
1816
-		$classification = self::CLASSIFICATION_PUBLIC;
1817
-		foreach($vObject->getComponents() as $component) {
1818
-			if ($component->name!=='VTIMEZONE') {
1819
-				$componentType = $component->name;
1820
-				$uid = (string)$component->UID;
1821
-				break;
1822
-			}
1823
-		}
1824
-		if (!$componentType) {
1825
-			throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
1826
-		}
1827
-		if ($componentType === 'VEVENT' && $component->DTSTART) {
1828
-			$firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
1829
-			// Finding the last occurrence is a bit harder
1830
-			if (!isset($component->RRULE)) {
1831
-				if (isset($component->DTEND)) {
1832
-					$lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
1833
-				} elseif (isset($component->DURATION)) {
1834
-					$endDate = clone $component->DTSTART->getDateTime();
1835
-					$endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
1836
-					$lastOccurrence = $endDate->getTimeStamp();
1837
-				} elseif (!$component->DTSTART->hasTime()) {
1838
-					$endDate = clone $component->DTSTART->getDateTime();
1839
-					$endDate->modify('+1 day');
1840
-					$lastOccurrence = $endDate->getTimeStamp();
1841
-				} else {
1842
-					$lastOccurrence = $firstOccurrence;
1843
-				}
1844
-			} else {
1845
-				$it = new EventIterator($vObject, (string)$component->UID);
1846
-				$maxDate = new \DateTime(self::MAX_DATE);
1847
-				if ($it->isInfinite()) {
1848
-					$lastOccurrence = $maxDate->getTimestamp();
1849
-				} else {
1850
-					$end = $it->getDtEnd();
1851
-					while($it->valid() && $end < $maxDate) {
1852
-						$end = $it->getDtEnd();
1853
-						$it->next();
1854
-
1855
-					}
1856
-					$lastOccurrence = $end->getTimestamp();
1857
-				}
1858
-
1859
-			}
1860
-		}
1861
-
1862
-		if ($component->CLASS) {
1863
-			$classification = CalDavBackend::CLASSIFICATION_PRIVATE;
1864
-			switch ($component->CLASS->getValue()) {
1865
-				case 'PUBLIC':
1866
-					$classification = CalDavBackend::CLASSIFICATION_PUBLIC;
1867
-					break;
1868
-				case 'CONFIDENTIAL':
1869
-					$classification = CalDavBackend::CLASSIFICATION_CONFIDENTIAL;
1870
-					break;
1871
-			}
1872
-		}
1873
-		return [
1874
-			'etag' => md5($calendarData),
1875
-			'size' => strlen($calendarData),
1876
-			'componentType' => $componentType,
1877
-			'firstOccurence' => is_null($firstOccurrence) ? null : max(0, $firstOccurrence),
1878
-			'lastOccurence'  => $lastOccurrence,
1879
-			'uid' => $uid,
1880
-			'classification' => $classification
1881
-		];
1882
-
1883
-	}
1884
-
1885
-	private function readBlob($cardData) {
1886
-		if (is_resource($cardData)) {
1887
-			return stream_get_contents($cardData);
1888
-		}
1889
-
1890
-		return $cardData;
1891
-	}
1892
-
1893
-	/**
1894
-	 * @param IShareable $shareable
1895
-	 * @param array $add
1896
-	 * @param array $remove
1897
-	 */
1898
-	public function updateShares($shareable, $add, $remove) {
1899
-		$calendarId = $shareable->getResourceId();
1900
-		$this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateShares', new GenericEvent(
1901
-			'\OCA\DAV\CalDAV\CalDavBackend::updateShares',
1902
-			[
1903
-				'calendarId' => $calendarId,
1904
-				'calendarData' => $this->getCalendarById($calendarId),
1905
-				'shares' => $this->getShares($calendarId),
1906
-				'add' => $add,
1907
-				'remove' => $remove,
1908
-			]));
1909
-		$this->sharingBackend->updateShares($shareable, $add, $remove);
1910
-	}
1911
-
1912
-	/**
1913
-	 * @param int $resourceId
1914
-	 * @return array
1915
-	 */
1916
-	public function getShares($resourceId) {
1917
-		return $this->sharingBackend->getShares($resourceId);
1918
-	}
1919
-
1920
-	/**
1921
-	 * @param boolean $value
1922
-	 * @param \OCA\DAV\CalDAV\Calendar $calendar
1923
-	 * @return string|null
1924
-	 */
1925
-	public function setPublishStatus($value, $calendar) {
1926
-		$query = $this->db->getQueryBuilder();
1927
-		if ($value) {
1928
-			$publicUri = $this->random->generate(16, ISecureRandom::CHAR_HUMAN_READABLE);
1929
-			$query->insert('dav_shares')
1930
-				->values([
1931
-					'principaluri' => $query->createNamedParameter($calendar->getPrincipalURI()),
1932
-					'type' => $query->createNamedParameter('calendar'),
1933
-					'access' => $query->createNamedParameter(self::ACCESS_PUBLIC),
1934
-					'resourceid' => $query->createNamedParameter($calendar->getResourceId()),
1935
-					'publicuri' => $query->createNamedParameter($publicUri)
1936
-				]);
1937
-			$query->execute();
1938
-			return $publicUri;
1939
-		}
1940
-		$query->delete('dav_shares')
1941
-			->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId())))
1942
-			->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)));
1943
-		$query->execute();
1944
-		return null;
1945
-	}
1946
-
1947
-	/**
1948
-	 * @param \OCA\DAV\CalDAV\Calendar $calendar
1949
-	 * @return mixed
1950
-	 */
1951
-	public function getPublishStatus($calendar) {
1952
-		$query = $this->db->getQueryBuilder();
1953
-		$result = $query->select('publicuri')
1954
-			->from('dav_shares')
1955
-			->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId())))
1956
-			->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
1957
-			->execute();
1958
-
1959
-		$row = $result->fetch();
1960
-		$result->closeCursor();
1961
-		return $row ? reset($row) : false;
1962
-	}
1963
-
1964
-	/**
1965
-	 * @param int $resourceId
1966
-	 * @param array $acl
1967
-	 * @return array
1968
-	 */
1969
-	public function applyShareAcl($resourceId, $acl) {
1970
-		return $this->sharingBackend->applyShareAcl($resourceId, $acl);
1971
-	}
1972
-
1973
-
1974
-
1975
-	/**
1976
-	 * update properties table
1977
-	 *
1978
-	 * @param int $calendarId
1979
-	 * @param string $objectUri
1980
-	 * @param string $calendarData
1981
-	 */
1982
-	public function updateProperties($calendarId, $objectUri, $calendarData) {
1983
-		$objectId = $this->getCalendarObjectId($calendarId, $objectUri);
1984
-
1985
-		try {
1986
-			$vCalendar = $this->readCalendarData($calendarData);
1987
-		} catch (\Exception $ex) {
1988
-			return;
1989
-		}
1990
-
1991
-		$this->purgeProperties($calendarId, $objectId);
1992
-
1993
-		$query = $this->db->getQueryBuilder();
1994
-		$query->insert($this->dbObjectPropertiesTable)
1995
-			->values(
1996
-				[
1997
-					'calendarid' => $query->createNamedParameter($calendarId),
1998
-					'objectid' => $query->createNamedParameter($objectId),
1999
-					'name' => $query->createParameter('name'),
2000
-					'parameter' => $query->createParameter('parameter'),
2001
-					'value' => $query->createParameter('value'),
2002
-				]
2003
-			);
2004
-
2005
-		$indexComponents = ['VEVENT', 'VJOURNAL', 'VTODO'];
2006
-		foreach ($vCalendar->getComponents() as $component) {
2007
-			if (!in_array($component->name, $indexComponents)) {
2008
-				continue;
2009
-			}
2010
-
2011
-			foreach ($component->children() as $property) {
2012
-				if (in_array($property->name, self::$indexProperties)) {
2013
-					$value = $property->getValue();
2014
-					// is this a shitty db?
2015
-					if (!$this->db->supports4ByteText()) {
2016
-						$value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value);
2017
-					}
2018
-					$value = substr($value, 0, 254);
2019
-
2020
-					$query->setParameter('name', $property->name);
2021
-					$query->setParameter('parameter', null);
2022
-					$query->setParameter('value', $value);
2023
-					$query->execute();
2024
-				}
2025
-
2026
-				if (in_array($property->name, array_keys(self::$indexParameters))) {
2027
-					$parameters = $property->parameters();
2028
-					$indexedParametersForProperty = self::$indexParameters[$property->name];
2029
-
2030
-					foreach ($parameters as $key => $value) {
2031
-						if (in_array($key, $indexedParametersForProperty)) {
2032
-							// is this a shitty db?
2033
-							if ($this->db->supports4ByteText()) {
2034
-								$value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value);
2035
-							}
2036
-							$value = substr($value, 0, 254);
2037
-
2038
-							$query->setParameter('name', $property->name);
2039
-							$query->setParameter('parameter', substr($key, 0, 254));
2040
-							$query->setParameter('value', substr($value, 0, 254));
2041
-							$query->execute();
2042
-						}
2043
-					}
2044
-				}
2045
-			}
2046
-		}
2047
-	}
2048
-
2049
-	/**
2050
-	 * read VCalendar data into a VCalendar object
2051
-	 *
2052
-	 * @param string $objectData
2053
-	 * @return VCalendar
2054
-	 */
2055
-	protected function readCalendarData($objectData) {
2056
-		return Reader::read($objectData);
2057
-	}
2058
-
2059
-	/**
2060
-	 * delete all properties from a given calendar object
2061
-	 *
2062
-	 * @param int $calendarId
2063
-	 * @param int $objectId
2064
-	 */
2065
-	protected function purgeProperties($calendarId, $objectId) {
2066
-		$query = $this->db->getQueryBuilder();
2067
-		$query->delete($this->dbObjectPropertiesTable)
2068
-			->where($query->expr()->eq('objectid', $query->createNamedParameter($objectId)))
2069
-			->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
2070
-		$query->execute();
2071
-	}
2072
-
2073
-	/**
2074
-	 * get ID from a given calendar object
2075
-	 *
2076
-	 * @param int $calendarId
2077
-	 * @param string $uri
2078
-	 * @return int
2079
-	 */
2080
-	protected function getCalendarObjectId($calendarId, $uri) {
2081
-		$query = $this->db->getQueryBuilder();
2082
-		$query->select('id')->from('calendarobjects')
2083
-			->where($query->expr()->eq('uri', $query->createNamedParameter($uri)))
2084
-			->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
2085
-
2086
-		$result = $query->execute();
2087
-		$objectIds = $result->fetch();
2088
-		$result->closeCursor();
2089
-
2090
-		if (!isset($objectIds['id'])) {
2091
-			throw new \InvalidArgumentException('Calendarobject does not exists: ' . $uri);
2092
-		}
2093
-
2094
-		return (int)$objectIds['id'];
2095
-	}
2096
-
2097
-	private function convertPrincipal($principalUri, $toV2) {
2098
-		if ($this->principalBackend->getPrincipalPrefix() === 'principals') {
2099
-			list(, $name) = URLUtil::splitPath($principalUri);
2100
-			if ($toV2 === true) {
2101
-				return "principals/users/$name";
2102
-			}
2103
-			return "principals/$name";
2104
-		}
2105
-		return $principalUri;
2106
-	}
2107
-
2108
-	private function addOwnerPrincipal(&$calendarInfo) {
2109
-		$ownerPrincipalKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal';
2110
-		$displaynameKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}owner-displayname';
2111
-		if (isset($calendarInfo[$ownerPrincipalKey])) {
2112
-			$uri = $calendarInfo[$ownerPrincipalKey];
2113
-		} else {
2114
-			$uri = $calendarInfo['principaluri'];
2115
-		}
2116
-
2117
-		$principalInformation = $this->principalBackend->getPrincipalByPath($uri);
2118
-		if (isset($principalInformation['{DAV:}displayname'])) {
2119
-			$calendarInfo[$displaynameKey] = $principalInformation['{DAV:}displayname'];
2120
-		}
2121
-	}
412
+    /**
413
+     * @return array
414
+     */
415
+    public function getPublicCalendars() {
416
+        $fields = array_values($this->propertyMap);
417
+        $fields[] = 'a.id';
418
+        $fields[] = 'a.uri';
419
+        $fields[] = 'a.synctoken';
420
+        $fields[] = 'a.components';
421
+        $fields[] = 'a.principaluri';
422
+        $fields[] = 'a.transparent';
423
+        $fields[] = 's.access';
424
+        $fields[] = 's.publicuri';
425
+        $calendars = [];
426
+        $query = $this->db->getQueryBuilder();
427
+        $result = $query->select($fields)
428
+            ->from('dav_shares', 's')
429
+            ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
430
+            ->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
431
+            ->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar')))
432
+            ->execute();
433
+
434
+        while($row = $result->fetch()) {
435
+            list(, $name) = URLUtil::splitPath($row['principaluri']);
436
+            $row['displayname'] = $row['displayname'] . "($name)";
437
+            $components = [];
438
+            if ($row['components']) {
439
+                $components = explode(',',$row['components']);
440
+            }
441
+            $calendar = [
442
+                'id' => $row['id'],
443
+                'uri' => $row['publicuri'],
444
+                'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
445
+                '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
446
+                '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
447
+                '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
448
+                '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
449
+                '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], $this->legacyEndpoint),
450
+                '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
451
+                '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
452
+            ];
453
+
454
+            foreach($this->propertyMap as $xmlName=>$dbName) {
455
+                $calendar[$xmlName] = $row[$dbName];
456
+            }
457
+
458
+            $this->addOwnerPrincipal($calendar);
459
+
460
+            if (!isset($calendars[$calendar['id']])) {
461
+                $calendars[$calendar['id']] = $calendar;
462
+            }
463
+        }
464
+        $result->closeCursor();
465
+
466
+        return array_values($calendars);
467
+    }
468
+
469
+    /**
470
+     * @param string $uri
471
+     * @return array
472
+     * @throws NotFound
473
+     */
474
+    public function getPublicCalendar($uri) {
475
+        $fields = array_values($this->propertyMap);
476
+        $fields[] = 'a.id';
477
+        $fields[] = 'a.uri';
478
+        $fields[] = 'a.synctoken';
479
+        $fields[] = 'a.components';
480
+        $fields[] = 'a.principaluri';
481
+        $fields[] = 'a.transparent';
482
+        $fields[] = 's.access';
483
+        $fields[] = 's.publicuri';
484
+        $query = $this->db->getQueryBuilder();
485
+        $result = $query->select($fields)
486
+            ->from('dav_shares', 's')
487
+            ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
488
+            ->where($query->expr()->in('s.access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
489
+            ->andWhere($query->expr()->eq('s.type', $query->createNamedParameter('calendar')))
490
+            ->andWhere($query->expr()->eq('s.publicuri', $query->createNamedParameter($uri)))
491
+            ->execute();
492
+
493
+        $row = $result->fetch(\PDO::FETCH_ASSOC);
494
+
495
+        $result->closeCursor();
496
+
497
+        if ($row === false) {
498
+            throw new NotFound('Node with name \'' . $uri . '\' could not be found');
499
+        }
500
+
501
+        list(, $name) = URLUtil::splitPath($row['principaluri']);
502
+        $row['displayname'] = $row['displayname'] . ' ' . "($name)";
503
+        $components = [];
504
+        if ($row['components']) {
505
+            $components = explode(',',$row['components']);
506
+        }
507
+        $calendar = [
508
+            'id' => $row['id'],
509
+            'uri' => $row['publicuri'],
510
+            'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
511
+            '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
512
+            '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
513
+            '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
514
+            '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
515
+            '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
516
+            '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => (int)$row['access'] === Backend::ACCESS_READ,
517
+            '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}public' => (int)$row['access'] === self::ACCESS_PUBLIC,
518
+        ];
519
+
520
+        foreach($this->propertyMap as $xmlName=>$dbName) {
521
+            $calendar[$xmlName] = $row[$dbName];
522
+        }
523
+
524
+        $this->addOwnerPrincipal($calendar);
525
+
526
+        return $calendar;
527
+
528
+    }
529
+
530
+    /**
531
+     * @param string $principal
532
+     * @param string $uri
533
+     * @return array|null
534
+     */
535
+    public function getCalendarByUri($principal, $uri) {
536
+        $fields = array_values($this->propertyMap);
537
+        $fields[] = 'id';
538
+        $fields[] = 'uri';
539
+        $fields[] = 'synctoken';
540
+        $fields[] = 'components';
541
+        $fields[] = 'principaluri';
542
+        $fields[] = 'transparent';
543
+
544
+        // Making fields a comma-delimited list
545
+        $query = $this->db->getQueryBuilder();
546
+        $query->select($fields)->from('calendars')
547
+            ->where($query->expr()->eq('uri', $query->createNamedParameter($uri)))
548
+            ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal)))
549
+            ->setMaxResults(1);
550
+        $stmt = $query->execute();
551
+
552
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
553
+        $stmt->closeCursor();
554
+        if ($row === false) {
555
+            return null;
556
+        }
557
+
558
+        $components = [];
559
+        if ($row['components']) {
560
+            $components = explode(',',$row['components']);
561
+        }
562
+
563
+        $calendar = [
564
+            'id' => $row['id'],
565
+            'uri' => $row['uri'],
566
+            'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
567
+            '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
568
+            '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
569
+            '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
570
+            '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
571
+        ];
572
+
573
+        foreach($this->propertyMap as $xmlName=>$dbName) {
574
+            $calendar[$xmlName] = $row[$dbName];
575
+        }
576
+
577
+        $this->addOwnerPrincipal($calendar);
578
+
579
+        return $calendar;
580
+    }
581
+
582
+    public function getCalendarById($calendarId) {
583
+        $fields = array_values($this->propertyMap);
584
+        $fields[] = 'id';
585
+        $fields[] = 'uri';
586
+        $fields[] = 'synctoken';
587
+        $fields[] = 'components';
588
+        $fields[] = 'principaluri';
589
+        $fields[] = 'transparent';
590
+
591
+        // Making fields a comma-delimited list
592
+        $query = $this->db->getQueryBuilder();
593
+        $query->select($fields)->from('calendars')
594
+            ->where($query->expr()->eq('id', $query->createNamedParameter($calendarId)))
595
+            ->setMaxResults(1);
596
+        $stmt = $query->execute();
597
+
598
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
599
+        $stmt->closeCursor();
600
+        if ($row === false) {
601
+            return null;
602
+        }
603
+
604
+        $components = [];
605
+        if ($row['components']) {
606
+            $components = explode(',',$row['components']);
607
+        }
608
+
609
+        $calendar = [
610
+            'id' => $row['id'],
611
+            'uri' => $row['uri'],
612
+            'principaluri' => $this->convertPrincipal($row['principaluri'], !$this->legacyEndpoint),
613
+            '{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
614
+            '{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
615
+            '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
616
+            '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
617
+        ];
618
+
619
+        foreach($this->propertyMap as $xmlName=>$dbName) {
620
+            $calendar[$xmlName] = $row[$dbName];
621
+        }
622
+
623
+        $this->addOwnerPrincipal($calendar);
624
+
625
+        return $calendar;
626
+    }
627
+
628
+    /**
629
+     * Creates a new calendar for a principal.
630
+     *
631
+     * If the creation was a success, an id must be returned that can be used to reference
632
+     * this calendar in other methods, such as updateCalendar.
633
+     *
634
+     * @param string $principalUri
635
+     * @param string $calendarUri
636
+     * @param array $properties
637
+     * @return int
638
+     */
639
+    function createCalendar($principalUri, $calendarUri, array $properties) {
640
+        $values = [
641
+            'principaluri' => $this->convertPrincipal($principalUri, true),
642
+            'uri'          => $calendarUri,
643
+            'synctoken'    => 1,
644
+            'transparent'  => 0,
645
+            'components'   => 'VEVENT,VTODO',
646
+            'displayname'  => $calendarUri
647
+        ];
648
+
649
+        // Default value
650
+        $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
651
+        if (isset($properties[$sccs])) {
652
+            if (!($properties[$sccs] instanceof SupportedCalendarComponentSet)) {
653
+                throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet');
654
+            }
655
+            $values['components'] = implode(',',$properties[$sccs]->getValue());
656
+        }
657
+        $transp = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp';
658
+        if (isset($properties[$transp])) {
659
+            $values['transparent'] = (int) ($properties[$transp]->getValue() === 'transparent');
660
+        }
661
+
662
+        foreach($this->propertyMap as $xmlName=>$dbName) {
663
+            if (isset($properties[$xmlName])) {
664
+                $values[$dbName] = $properties[$xmlName];
665
+            }
666
+        }
667
+
668
+        $query = $this->db->getQueryBuilder();
669
+        $query->insert('calendars');
670
+        foreach($values as $column => $value) {
671
+            $query->setValue($column, $query->createNamedParameter($value));
672
+        }
673
+        $query->execute();
674
+        $calendarId = $query->getLastInsertId();
675
+
676
+        $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendar', new GenericEvent(
677
+            '\OCA\DAV\CalDAV\CalDavBackend::createCalendar',
678
+            [
679
+                'calendarId' => $calendarId,
680
+                'calendarData' => $this->getCalendarById($calendarId),
681
+        ]));
682
+
683
+        return $calendarId;
684
+    }
685
+
686
+    /**
687
+     * Updates properties for a calendar.
688
+     *
689
+     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
690
+     * To do the actual updates, you must tell this object which properties
691
+     * you're going to process with the handle() method.
692
+     *
693
+     * Calling the handle method is like telling the PropPatch object "I
694
+     * promise I can handle updating this property".
695
+     *
696
+     * Read the PropPatch documentation for more info and examples.
697
+     *
698
+     * @param PropPatch $propPatch
699
+     * @return void
700
+     */
701
+    function updateCalendar($calendarId, PropPatch $propPatch) {
702
+        $supportedProperties = array_keys($this->propertyMap);
703
+        $supportedProperties[] = '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp';
704
+
705
+        $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) {
706
+            $newValues = [];
707
+            foreach ($mutations as $propertyName => $propertyValue) {
708
+
709
+                switch ($propertyName) {
710
+                    case '{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' :
711
+                        $fieldName = 'transparent';
712
+                        $newValues[$fieldName] = (int) ($propertyValue->getValue() === 'transparent');
713
+                        break;
714
+                    default :
715
+                        $fieldName = $this->propertyMap[$propertyName];
716
+                        $newValues[$fieldName] = $propertyValue;
717
+                        break;
718
+                }
719
+
720
+            }
721
+            $query = $this->db->getQueryBuilder();
722
+            $query->update('calendars');
723
+            foreach ($newValues as $fieldName => $value) {
724
+                $query->set($fieldName, $query->createNamedParameter($value));
725
+            }
726
+            $query->where($query->expr()->eq('id', $query->createNamedParameter($calendarId)));
727
+            $query->execute();
728
+
729
+            $this->addChange($calendarId, "", 2);
730
+
731
+            $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendar', new GenericEvent(
732
+                '\OCA\DAV\CalDAV\CalDavBackend::updateCalendar',
733
+                [
734
+                    'calendarId' => $calendarId,
735
+                    'calendarData' => $this->getCalendarById($calendarId),
736
+                    'shares' => $this->getShares($calendarId),
737
+                    'propertyMutations' => $mutations,
738
+            ]));
739
+
740
+            return true;
741
+        });
742
+    }
743
+
744
+    /**
745
+     * Delete a calendar and all it's objects
746
+     *
747
+     * @param mixed $calendarId
748
+     * @return void
749
+     */
750
+    function deleteCalendar($calendarId) {
751
+        $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', new GenericEvent(
752
+            '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar',
753
+            [
754
+                'calendarId' => $calendarId,
755
+                'calendarData' => $this->getCalendarById($calendarId),
756
+                'shares' => $this->getShares($calendarId),
757
+        ]));
758
+
759
+        $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?');
760
+        $stmt->execute([$calendarId]);
761
+
762
+        $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendars` WHERE `id` = ?');
763
+        $stmt->execute([$calendarId]);
764
+
765
+        $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarchanges` WHERE `calendarid` = ?');
766
+        $stmt->execute([$calendarId]);
767
+
768
+        $this->sharingBackend->deleteAllShares($calendarId);
769
+
770
+        $query = $this->db->getQueryBuilder();
771
+        $query->delete($this->dbObjectPropertiesTable)
772
+            ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
773
+            ->execute();
774
+    }
775
+
776
+    /**
777
+     * Delete all of an user's shares
778
+     *
779
+     * @param string $principaluri
780
+     * @return void
781
+     */
782
+    function deleteAllSharesByUser($principaluri) {
783
+        $this->sharingBackend->deleteAllSharesByUser($principaluri);
784
+    }
785
+
786
+    /**
787
+     * Returns all calendar objects within a calendar.
788
+     *
789
+     * Every item contains an array with the following keys:
790
+     *   * calendardata - The iCalendar-compatible calendar data
791
+     *   * uri - a unique key which will be used to construct the uri. This can
792
+     *     be any arbitrary string, but making sure it ends with '.ics' is a
793
+     *     good idea. This is only the basename, or filename, not the full
794
+     *     path.
795
+     *   * lastmodified - a timestamp of the last modification time
796
+     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
797
+     *   '"abcdef"')
798
+     *   * size - The size of the calendar objects, in bytes.
799
+     *   * component - optional, a string containing the type of object, such
800
+     *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
801
+     *     the Content-Type header.
802
+     *
803
+     * Note that the etag is optional, but it's highly encouraged to return for
804
+     * speed reasons.
805
+     *
806
+     * The calendardata is also optional. If it's not returned
807
+     * 'getCalendarObject' will be called later, which *is* expected to return
808
+     * calendardata.
809
+     *
810
+     * If neither etag or size are specified, the calendardata will be
811
+     * used/fetched to determine these numbers. If both are specified the
812
+     * amount of times this is needed is reduced by a great degree.
813
+     *
814
+     * @param mixed $calendarId
815
+     * @return array
816
+     */
817
+    function getCalendarObjects($calendarId) {
818
+        $query = $this->db->getQueryBuilder();
819
+        $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'componenttype', 'classification'])
820
+            ->from('calendarobjects')
821
+            ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
822
+        $stmt = $query->execute();
823
+
824
+        $result = [];
825
+        foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
826
+            $result[] = [
827
+                    'id'           => $row['id'],
828
+                    'uri'          => $row['uri'],
829
+                    'lastmodified' => $row['lastmodified'],
830
+                    'etag'         => '"' . $row['etag'] . '"',
831
+                    'calendarid'   => $row['calendarid'],
832
+                    'size'         => (int)$row['size'],
833
+                    'component'    => strtolower($row['componenttype']),
834
+                    'classification'=> (int)$row['classification']
835
+            ];
836
+        }
837
+
838
+        return $result;
839
+    }
840
+
841
+    /**
842
+     * Returns information from a single calendar object, based on it's object
843
+     * uri.
844
+     *
845
+     * The object uri is only the basename, or filename and not a full path.
846
+     *
847
+     * The returned array must have the same keys as getCalendarObjects. The
848
+     * 'calendardata' object is required here though, while it's not required
849
+     * for getCalendarObjects.
850
+     *
851
+     * This method must return null if the object did not exist.
852
+     *
853
+     * @param mixed $calendarId
854
+     * @param string $objectUri
855
+     * @return array|null
856
+     */
857
+    function getCalendarObject($calendarId, $objectUri) {
858
+
859
+        $query = $this->db->getQueryBuilder();
860
+        $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
861
+                ->from('calendarobjects')
862
+                ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
863
+                ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)));
864
+        $stmt = $query->execute();
865
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
866
+
867
+        if(!$row) return null;
868
+
869
+        return [
870
+                'id'            => $row['id'],
871
+                'uri'           => $row['uri'],
872
+                'lastmodified'  => $row['lastmodified'],
873
+                'etag'          => '"' . $row['etag'] . '"',
874
+                'calendarid'    => $row['calendarid'],
875
+                'size'          => (int)$row['size'],
876
+                'calendardata'  => $this->readBlob($row['calendardata']),
877
+                'component'     => strtolower($row['componenttype']),
878
+                'classification'=> (int)$row['classification']
879
+        ];
880
+    }
881
+
882
+    /**
883
+     * Returns a list of calendar objects.
884
+     *
885
+     * This method should work identical to getCalendarObject, but instead
886
+     * return all the calendar objects in the list as an array.
887
+     *
888
+     * If the backend supports this, it may allow for some speed-ups.
889
+     *
890
+     * @param mixed $calendarId
891
+     * @param string[] $uris
892
+     * @return array
893
+     */
894
+    function getMultipleCalendarObjects($calendarId, array $uris) {
895
+        if (empty($uris)) {
896
+            return [];
897
+        }
898
+
899
+        $chunks = array_chunk($uris, 100);
900
+        $objects = [];
901
+
902
+        $query = $this->db->getQueryBuilder();
903
+        $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification'])
904
+            ->from('calendarobjects')
905
+            ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
906
+            ->andWhere($query->expr()->in('uri', $query->createParameter('uri')));
907
+
908
+        foreach ($chunks as $uris) {
909
+            $query->setParameter('uri', $uris, IQueryBuilder::PARAM_STR_ARRAY);
910
+            $result = $query->execute();
911
+
912
+            while ($row = $result->fetch()) {
913
+                $objects[] = [
914
+                    'id'           => $row['id'],
915
+                    'uri'          => $row['uri'],
916
+                    'lastmodified' => $row['lastmodified'],
917
+                    'etag'         => '"' . $row['etag'] . '"',
918
+                    'calendarid'   => $row['calendarid'],
919
+                    'size'         => (int)$row['size'],
920
+                    'calendardata' => $this->readBlob($row['calendardata']),
921
+                    'component'    => strtolower($row['componenttype']),
922
+                    'classification' => (int)$row['classification']
923
+                ];
924
+            }
925
+            $result->closeCursor();
926
+        }
927
+        return $objects;
928
+    }
929
+
930
+    /**
931
+     * Creates a new calendar object.
932
+     *
933
+     * The object uri is only the basename, or filename and not a full path.
934
+     *
935
+     * It is possible return an etag from this function, which will be used in
936
+     * the response to this PUT request. Note that the ETag must be surrounded
937
+     * by double-quotes.
938
+     *
939
+     * However, you should only really return this ETag if you don't mangle the
940
+     * calendar-data. If the result of a subsequent GET to this object is not
941
+     * the exact same as this request body, you should omit the ETag.
942
+     *
943
+     * @param mixed $calendarId
944
+     * @param string $objectUri
945
+     * @param string $calendarData
946
+     * @return string
947
+     */
948
+    function createCalendarObject($calendarId, $objectUri, $calendarData) {
949
+        $extraData = $this->getDenormalizedData($calendarData);
950
+
951
+        $query = $this->db->getQueryBuilder();
952
+        $query->insert('calendarobjects')
953
+            ->values([
954
+                'calendarid' => $query->createNamedParameter($calendarId),
955
+                'uri' => $query->createNamedParameter($objectUri),
956
+                'calendardata' => $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB),
957
+                'lastmodified' => $query->createNamedParameter(time()),
958
+                'etag' => $query->createNamedParameter($extraData['etag']),
959
+                'size' => $query->createNamedParameter($extraData['size']),
960
+                'componenttype' => $query->createNamedParameter($extraData['componentType']),
961
+                'firstoccurence' => $query->createNamedParameter($extraData['firstOccurence']),
962
+                'lastoccurence' => $query->createNamedParameter($extraData['lastOccurence']),
963
+                'classification' => $query->createNamedParameter($extraData['classification']),
964
+                'uid' => $query->createNamedParameter($extraData['uid']),
965
+            ])
966
+            ->execute();
967
+
968
+        $this->updateProperties($calendarId, $objectUri, $calendarData);
969
+
970
+        $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', new GenericEvent(
971
+            '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
972
+            [
973
+                'calendarId' => $calendarId,
974
+                'calendarData' => $this->getCalendarById($calendarId),
975
+                'shares' => $this->getShares($calendarId),
976
+                'objectData' => $this->getCalendarObject($calendarId, $objectUri),
977
+            ]
978
+        ));
979
+        $this->addChange($calendarId, $objectUri, 1);
980
+
981
+        return '"' . $extraData['etag'] . '"';
982
+    }
983
+
984
+    /**
985
+     * Updates an existing calendarobject, based on it's uri.
986
+     *
987
+     * The object uri is only the basename, or filename and not a full path.
988
+     *
989
+     * It is possible return an etag from this function, which will be used in
990
+     * the response to this PUT request. Note that the ETag must be surrounded
991
+     * by double-quotes.
992
+     *
993
+     * However, you should only really return this ETag if you don't mangle the
994
+     * calendar-data. If the result of a subsequent GET to this object is not
995
+     * the exact same as this request body, you should omit the ETag.
996
+     *
997
+     * @param mixed $calendarId
998
+     * @param string $objectUri
999
+     * @param string $calendarData
1000
+     * @return string
1001
+     */
1002
+    function updateCalendarObject($calendarId, $objectUri, $calendarData) {
1003
+        $extraData = $this->getDenormalizedData($calendarData);
1004
+
1005
+        $query = $this->db->getQueryBuilder();
1006
+        $query->update('calendarobjects')
1007
+                ->set('calendardata', $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB))
1008
+                ->set('lastmodified', $query->createNamedParameter(time()))
1009
+                ->set('etag', $query->createNamedParameter($extraData['etag']))
1010
+                ->set('size', $query->createNamedParameter($extraData['size']))
1011
+                ->set('componenttype', $query->createNamedParameter($extraData['componentType']))
1012
+                ->set('firstoccurence', $query->createNamedParameter($extraData['firstOccurence']))
1013
+                ->set('lastoccurence', $query->createNamedParameter($extraData['lastOccurence']))
1014
+                ->set('classification', $query->createNamedParameter($extraData['classification']))
1015
+                ->set('uid', $query->createNamedParameter($extraData['uid']))
1016
+            ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
1017
+            ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1018
+            ->execute();
1019
+
1020
+        $this->updateProperties($calendarId, $objectUri, $calendarData);
1021
+
1022
+        $data = $this->getCalendarObject($calendarId, $objectUri);
1023
+        if (is_array($data)) {
1024
+            $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', new GenericEvent(
1025
+                '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject',
1026
+                [
1027
+                    'calendarId' => $calendarId,
1028
+                    'calendarData' => $this->getCalendarById($calendarId),
1029
+                    'shares' => $this->getShares($calendarId),
1030
+                    'objectData' => $data,
1031
+                ]
1032
+            ));
1033
+        }
1034
+        $this->addChange($calendarId, $objectUri, 2);
1035
+
1036
+        return '"' . $extraData['etag'] . '"';
1037
+    }
1038
+
1039
+    /**
1040
+     * @param int $calendarObjectId
1041
+     * @param int $classification
1042
+     */
1043
+    public function setClassification($calendarObjectId, $classification) {
1044
+        if (!in_array($classification, [
1045
+            self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL
1046
+        ])) {
1047
+            throw new \InvalidArgumentException();
1048
+        }
1049
+        $query = $this->db->getQueryBuilder();
1050
+        $query->update('calendarobjects')
1051
+            ->set('classification', $query->createNamedParameter($classification))
1052
+            ->where($query->expr()->eq('id', $query->createNamedParameter($calendarObjectId)))
1053
+            ->execute();
1054
+    }
1055
+
1056
+    /**
1057
+     * Deletes an existing calendar object.
1058
+     *
1059
+     * The object uri is only the basename, or filename and not a full path.
1060
+     *
1061
+     * @param mixed $calendarId
1062
+     * @param string $objectUri
1063
+     * @return void
1064
+     */
1065
+    function deleteCalendarObject($calendarId, $objectUri) {
1066
+        $data = $this->getCalendarObject($calendarId, $objectUri);
1067
+        if (is_array($data)) {
1068
+            $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', new GenericEvent(
1069
+                '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject',
1070
+                [
1071
+                    'calendarId' => $calendarId,
1072
+                    'calendarData' => $this->getCalendarById($calendarId),
1073
+                    'shares' => $this->getShares($calendarId),
1074
+                    'objectData' => $data,
1075
+                ]
1076
+            ));
1077
+        }
1078
+
1079
+        $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `uri` = ?');
1080
+        $stmt->execute([$calendarId, $objectUri]);
1081
+
1082
+        $this->purgeProperties($calendarId, $data['id']);
1083
+
1084
+        $this->addChange($calendarId, $objectUri, 3);
1085
+    }
1086
+
1087
+    /**
1088
+     * Performs a calendar-query on the contents of this calendar.
1089
+     *
1090
+     * The calendar-query is defined in RFC4791 : CalDAV. Using the
1091
+     * calendar-query it is possible for a client to request a specific set of
1092
+     * object, based on contents of iCalendar properties, date-ranges and
1093
+     * iCalendar component types (VTODO, VEVENT).
1094
+     *
1095
+     * This method should just return a list of (relative) urls that match this
1096
+     * query.
1097
+     *
1098
+     * The list of filters are specified as an array. The exact array is
1099
+     * documented by Sabre\CalDAV\CalendarQueryParser.
1100
+     *
1101
+     * Note that it is extremely likely that getCalendarObject for every path
1102
+     * returned from this method will be called almost immediately after. You
1103
+     * may want to anticipate this to speed up these requests.
1104
+     *
1105
+     * This method provides a default implementation, which parses *all* the
1106
+     * iCalendar objects in the specified calendar.
1107
+     *
1108
+     * This default may well be good enough for personal use, and calendars
1109
+     * that aren't very large. But if you anticipate high usage, big calendars
1110
+     * or high loads, you are strongly advised to optimize certain paths.
1111
+     *
1112
+     * The best way to do so is override this method and to optimize
1113
+     * specifically for 'common filters'.
1114
+     *
1115
+     * Requests that are extremely common are:
1116
+     *   * requests for just VEVENTS
1117
+     *   * requests for just VTODO
1118
+     *   * requests with a time-range-filter on either VEVENT or VTODO.
1119
+     *
1120
+     * ..and combinations of these requests. It may not be worth it to try to
1121
+     * handle every possible situation and just rely on the (relatively
1122
+     * easy to use) CalendarQueryValidator to handle the rest.
1123
+     *
1124
+     * Note that especially time-range-filters may be difficult to parse. A
1125
+     * time-range filter specified on a VEVENT must for instance also handle
1126
+     * recurrence rules correctly.
1127
+     * A good example of how to interprete all these filters can also simply
1128
+     * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
1129
+     * as possible, so it gives you a good idea on what type of stuff you need
1130
+     * to think of.
1131
+     *
1132
+     * @param mixed $calendarId
1133
+     * @param array $filters
1134
+     * @return array
1135
+     */
1136
+    function calendarQuery($calendarId, array $filters) {
1137
+        $componentType = null;
1138
+        $requirePostFilter = true;
1139
+        $timeRange = null;
1140
+
1141
+        // if no filters were specified, we don't need to filter after a query
1142
+        if (!$filters['prop-filters'] && !$filters['comp-filters']) {
1143
+            $requirePostFilter = false;
1144
+        }
1145
+
1146
+        // Figuring out if there's a component filter
1147
+        if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
1148
+            $componentType = $filters['comp-filters'][0]['name'];
1149
+
1150
+            // Checking if we need post-filters
1151
+            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
1152
+                $requirePostFilter = false;
1153
+            }
1154
+            // There was a time-range filter
1155
+            if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
1156
+                $timeRange = $filters['comp-filters'][0]['time-range'];
1157
+
1158
+                // If start time OR the end time is not specified, we can do a
1159
+                // 100% accurate mysql query.
1160
+                if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
1161
+                    $requirePostFilter = false;
1162
+                }
1163
+            }
1164
+
1165
+        }
1166
+        $columns = ['uri'];
1167
+        if ($requirePostFilter) {
1168
+            $columns = ['uri', 'calendardata'];
1169
+        }
1170
+        $query = $this->db->getQueryBuilder();
1171
+        $query->select($columns)
1172
+            ->from('calendarobjects')
1173
+            ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
1174
+
1175
+        if ($componentType) {
1176
+            $query->andWhere($query->expr()->eq('componenttype', $query->createNamedParameter($componentType)));
1177
+        }
1178
+
1179
+        if ($timeRange && $timeRange['start']) {
1180
+            $query->andWhere($query->expr()->gt('lastoccurence', $query->createNamedParameter($timeRange['start']->getTimeStamp())));
1181
+        }
1182
+        if ($timeRange && $timeRange['end']) {
1183
+            $query->andWhere($query->expr()->lt('firstoccurence', $query->createNamedParameter($timeRange['end']->getTimeStamp())));
1184
+        }
1185
+
1186
+        $stmt = $query->execute();
1187
+
1188
+        $result = [];
1189
+        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1190
+            if ($requirePostFilter) {
1191
+                if (!$this->validateFilterForObject($row, $filters)) {
1192
+                    continue;
1193
+                }
1194
+            }
1195
+            $result[] = $row['uri'];
1196
+        }
1197
+
1198
+        return $result;
1199
+    }
1200
+
1201
+    /**
1202
+     * custom Nextcloud search extension for CalDAV
1203
+     *
1204
+     * @param string $principalUri
1205
+     * @param array $filters
1206
+     * @param integer|null $limit
1207
+     * @param integer|null $offset
1208
+     * @return array
1209
+     */
1210
+    public function calendarSearch($principalUri, array $filters, $limit=null, $offset=null) {
1211
+        $calendars = $this->getCalendarsForUser($principalUri);
1212
+        $ownCalendars = [];
1213
+        $sharedCalendars = [];
1214
+
1215
+        $uriMapper = [];
1216
+
1217
+        foreach($calendars as $calendar) {
1218
+            if ($calendar['{http://owncloud.org/ns}owner-principal'] === $principalUri) {
1219
+                $ownCalendars[] = $calendar['id'];
1220
+            } else {
1221
+                $sharedCalendars[] = $calendar['id'];
1222
+            }
1223
+            $uriMapper[$calendar['id']] = $calendar['uri'];
1224
+        }
1225
+        if (count($ownCalendars) === 0 && count($sharedCalendars) === 0) {
1226
+            return [];
1227
+        }
1228
+
1229
+        $query = $this->db->getQueryBuilder();
1230
+        // Calendar id expressions
1231
+        $calendarExpressions = [];
1232
+        foreach($ownCalendars as $id) {
1233
+            $calendarExpressions[] = $query->expr()
1234
+                ->eq('c.calendarid', $query->createNamedParameter($id));
1235
+        }
1236
+        foreach($sharedCalendars as $id) {
1237
+            $calendarExpressions[] = $query->expr()->andX(
1238
+                $query->expr()->eq('c.calendarid',
1239
+                    $query->createNamedParameter($id)),
1240
+                $query->expr()->eq('c.classification',
1241
+                    $query->createNamedParameter(self::CLASSIFICATION_PUBLIC))
1242
+            );
1243
+        }
1244
+
1245
+        if (count($calendarExpressions) === 1) {
1246
+            $calExpr = $calendarExpressions[0];
1247
+        } else {
1248
+            $calExpr = call_user_func_array([$query->expr(), 'orX'], $calendarExpressions);
1249
+        }
1250
+
1251
+        // Component expressions
1252
+        $compExpressions = [];
1253
+        foreach($filters['comps'] as $comp) {
1254
+            $compExpressions[] = $query->expr()
1255
+                ->eq('c.componenttype', $query->createNamedParameter($comp));
1256
+        }
1257
+
1258
+        if (count($compExpressions) === 1) {
1259
+            $compExpr = $compExpressions[0];
1260
+        } else {
1261
+            $compExpr = call_user_func_array([$query->expr(), 'orX'], $compExpressions);
1262
+        }
1263
+
1264
+        if (!isset($filters['props'])) {
1265
+            $filters['props'] = [];
1266
+        }
1267
+        if (!isset($filters['params'])) {
1268
+            $filters['params'] = [];
1269
+        }
1270
+
1271
+        $propParamExpressions = [];
1272
+        foreach($filters['props'] as $prop) {
1273
+            $propParamExpressions[] = $query->expr()->andX(
1274
+                $query->expr()->eq('i.name', $query->createNamedParameter($prop)),
1275
+                $query->expr()->isNull('i.parameter')
1276
+            );
1277
+        }
1278
+        foreach($filters['params'] as $param) {
1279
+            $propParamExpressions[] = $query->expr()->andX(
1280
+                $query->expr()->eq('i.name', $query->createNamedParameter($param['property'])),
1281
+                $query->expr()->eq('i.parameter', $query->createNamedParameter($param['parameter']))
1282
+            );
1283
+        }
1284
+
1285
+        if (count($propParamExpressions) === 1) {
1286
+            $propParamExpr = $propParamExpressions[0];
1287
+        } else {
1288
+            $propParamExpr = call_user_func_array([$query->expr(), 'orX'], $propParamExpressions);
1289
+        }
1290
+
1291
+        $query->select(['c.calendarid', 'c.uri'])
1292
+            ->from($this->dbObjectPropertiesTable, 'i')
1293
+            ->join('i', 'calendarobjects', 'c', $query->expr()->eq('i.objectid', 'c.id'))
1294
+            ->where($calExpr)
1295
+            ->andWhere($compExpr)
1296
+            ->andWhere($propParamExpr)
1297
+            ->andWhere($query->expr()->iLike('i.value',
1298
+                $query->createNamedParameter('%'.$this->db->escapeLikeParameter($filters['search-term']).'%')));
1299
+
1300
+        if ($offset) {
1301
+            $query->setFirstResult($offset);
1302
+        }
1303
+        if ($limit) {
1304
+            $query->setMaxResults($limit);
1305
+        }
1306
+
1307
+        $stmt = $query->execute();
1308
+
1309
+        $result = [];
1310
+        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1311
+            $path = $uriMapper[$row['calendarid']] . '/' . $row['uri'];
1312
+            if (!in_array($path, $result)) {
1313
+                $result[] = $path;
1314
+            }
1315
+        }
1316
+
1317
+        return $result;
1318
+    }
1319
+
1320
+    /**
1321
+     * Searches through all of a users calendars and calendar objects to find
1322
+     * an object with a specific UID.
1323
+     *
1324
+     * This method should return the path to this object, relative to the
1325
+     * calendar home, so this path usually only contains two parts:
1326
+     *
1327
+     * calendarpath/objectpath.ics
1328
+     *
1329
+     * If the uid is not found, return null.
1330
+     *
1331
+     * This method should only consider * objects that the principal owns, so
1332
+     * any calendars owned by other principals that also appear in this
1333
+     * collection should be ignored.
1334
+     *
1335
+     * @param string $principalUri
1336
+     * @param string $uid
1337
+     * @return string|null
1338
+     */
1339
+    function getCalendarObjectByUID($principalUri, $uid) {
1340
+
1341
+        $query = $this->db->getQueryBuilder();
1342
+        $query->selectAlias('c.uri', 'calendaruri')->selectAlias('co.uri', 'objecturi')
1343
+            ->from('calendarobjects', 'co')
1344
+            ->leftJoin('co', 'calendars', 'c', $query->expr()->eq('co.calendarid', 'c.id'))
1345
+            ->where($query->expr()->eq('c.principaluri', $query->createNamedParameter($principalUri)))
1346
+            ->andWhere($query->expr()->eq('co.uid', $query->createNamedParameter($uid)));
1347
+
1348
+        $stmt = $query->execute();
1349
+
1350
+        if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1351
+            return $row['calendaruri'] . '/' . $row['objecturi'];
1352
+        }
1353
+
1354
+        return null;
1355
+    }
1356
+
1357
+    /**
1358
+     * The getChanges method returns all the changes that have happened, since
1359
+     * the specified syncToken in the specified calendar.
1360
+     *
1361
+     * This function should return an array, such as the following:
1362
+     *
1363
+     * [
1364
+     *   'syncToken' => 'The current synctoken',
1365
+     *   'added'   => [
1366
+     *      'new.txt',
1367
+     *   ],
1368
+     *   'modified'   => [
1369
+     *      'modified.txt',
1370
+     *   ],
1371
+     *   'deleted' => [
1372
+     *      'foo.php.bak',
1373
+     *      'old.txt'
1374
+     *   ]
1375
+     * );
1376
+     *
1377
+     * The returned syncToken property should reflect the *current* syncToken
1378
+     * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
1379
+     * property This is * needed here too, to ensure the operation is atomic.
1380
+     *
1381
+     * If the $syncToken argument is specified as null, this is an initial
1382
+     * sync, and all members should be reported.
1383
+     *
1384
+     * The modified property is an array of nodenames that have changed since
1385
+     * the last token.
1386
+     *
1387
+     * The deleted property is an array with nodenames, that have been deleted
1388
+     * from collection.
1389
+     *
1390
+     * The $syncLevel argument is basically the 'depth' of the report. If it's
1391
+     * 1, you only have to report changes that happened only directly in
1392
+     * immediate descendants. If it's 2, it should also include changes from
1393
+     * the nodes below the child collections. (grandchildren)
1394
+     *
1395
+     * The $limit argument allows a client to specify how many results should
1396
+     * be returned at most. If the limit is not specified, it should be treated
1397
+     * as infinite.
1398
+     *
1399
+     * If the limit (infinite or not) is higher than you're willing to return,
1400
+     * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
1401
+     *
1402
+     * If the syncToken is expired (due to data cleanup) or unknown, you must
1403
+     * return null.
1404
+     *
1405
+     * The limit is 'suggestive'. You are free to ignore it.
1406
+     *
1407
+     * @param string $calendarId
1408
+     * @param string $syncToken
1409
+     * @param int $syncLevel
1410
+     * @param int $limit
1411
+     * @return array
1412
+     */
1413
+    function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) {
1414
+        // Current synctoken
1415
+        $stmt = $this->db->prepare('SELECT `synctoken` FROM `*PREFIX*calendars` WHERE `id` = ?');
1416
+        $stmt->execute([ $calendarId ]);
1417
+        $currentToken = $stmt->fetchColumn(0);
1418
+
1419
+        if (is_null($currentToken)) {
1420
+            return null;
1421
+        }
1422
+
1423
+        $result = [
1424
+            'syncToken' => $currentToken,
1425
+            'added'     => [],
1426
+            'modified'  => [],
1427
+            'deleted'   => [],
1428
+        ];
1429
+
1430
+        if ($syncToken) {
1431
+
1432
+            $query = "SELECT `uri`, `operation` FROM `*PREFIX*calendarchanges` WHERE `synctoken` >= ? AND `synctoken` < ? AND `calendarid` = ? ORDER BY `synctoken`";
1433
+            if ($limit>0) {
1434
+                $query.= " `LIMIT` " . (int)$limit;
1435
+            }
1436
+
1437
+            // Fetching all changes
1438
+            $stmt = $this->db->prepare($query);
1439
+            $stmt->execute([$syncToken, $currentToken, $calendarId]);
1440
+
1441
+            $changes = [];
1442
+
1443
+            // This loop ensures that any duplicates are overwritten, only the
1444
+            // last change on a node is relevant.
1445
+            while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1446
+
1447
+                $changes[$row['uri']] = $row['operation'];
1448
+
1449
+            }
1450
+
1451
+            foreach($changes as $uri => $operation) {
1452
+
1453
+                switch($operation) {
1454
+                    case 1 :
1455
+                        $result['added'][] = $uri;
1456
+                        break;
1457
+                    case 2 :
1458
+                        $result['modified'][] = $uri;
1459
+                        break;
1460
+                    case 3 :
1461
+                        $result['deleted'][] = $uri;
1462
+                        break;
1463
+                }
1464
+
1465
+            }
1466
+        } else {
1467
+            // No synctoken supplied, this is the initial sync.
1468
+            $query = "SELECT `uri` FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ?";
1469
+            $stmt = $this->db->prepare($query);
1470
+            $stmt->execute([$calendarId]);
1471
+
1472
+            $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
1473
+        }
1474
+        return $result;
1475
+
1476
+    }
1477
+
1478
+    /**
1479
+     * Returns a list of subscriptions for a principal.
1480
+     *
1481
+     * Every subscription is an array with the following keys:
1482
+     *  * id, a unique id that will be used by other functions to modify the
1483
+     *    subscription. This can be the same as the uri or a database key.
1484
+     *  * uri. This is just the 'base uri' or 'filename' of the subscription.
1485
+     *  * principaluri. The owner of the subscription. Almost always the same as
1486
+     *    principalUri passed to this method.
1487
+     *
1488
+     * Furthermore, all the subscription info must be returned too:
1489
+     *
1490
+     * 1. {DAV:}displayname
1491
+     * 2. {http://apple.com/ns/ical/}refreshrate
1492
+     * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
1493
+     *    should not be stripped).
1494
+     * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
1495
+     *    should not be stripped).
1496
+     * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
1497
+     *    attachments should not be stripped).
1498
+     * 6. {http://calendarserver.org/ns/}source (Must be a
1499
+     *     Sabre\DAV\Property\Href).
1500
+     * 7. {http://apple.com/ns/ical/}calendar-color
1501
+     * 8. {http://apple.com/ns/ical/}calendar-order
1502
+     * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
1503
+     *    (should just be an instance of
1504
+     *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
1505
+     *    default components).
1506
+     *
1507
+     * @param string $principalUri
1508
+     * @return array
1509
+     */
1510
+    function getSubscriptionsForUser($principalUri) {
1511
+        $fields = array_values($this->subscriptionPropertyMap);
1512
+        $fields[] = 'id';
1513
+        $fields[] = 'uri';
1514
+        $fields[] = 'source';
1515
+        $fields[] = 'principaluri';
1516
+        $fields[] = 'lastmodified';
1517
+
1518
+        $query = $this->db->getQueryBuilder();
1519
+        $query->select($fields)
1520
+            ->from('calendarsubscriptions')
1521
+            ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1522
+            ->orderBy('calendarorder', 'asc');
1523
+        $stmt =$query->execute();
1524
+
1525
+        $subscriptions = [];
1526
+        while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1527
+
1528
+            $subscription = [
1529
+                'id'           => $row['id'],
1530
+                'uri'          => $row['uri'],
1531
+                'principaluri' => $row['principaluri'],
1532
+                'source'       => $row['source'],
1533
+                'lastmodified' => $row['lastmodified'],
1534
+
1535
+                '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1536
+            ];
1537
+
1538
+            foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
1539
+                if (!is_null($row[$dbName])) {
1540
+                    $subscription[$xmlName] = $row[$dbName];
1541
+                }
1542
+            }
1543
+
1544
+            $subscriptions[] = $subscription;
1545
+
1546
+        }
1547
+
1548
+        return $subscriptions;
1549
+    }
1550
+
1551
+    /**
1552
+     * Creates a new subscription for a principal.
1553
+     *
1554
+     * If the creation was a success, an id must be returned that can be used to reference
1555
+     * this subscription in other methods, such as updateSubscription.
1556
+     *
1557
+     * @param string $principalUri
1558
+     * @param string $uri
1559
+     * @param array $properties
1560
+     * @return mixed
1561
+     */
1562
+    function createSubscription($principalUri, $uri, array $properties) {
1563
+
1564
+        if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1565
+            throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1566
+        }
1567
+
1568
+        $values = [
1569
+            'principaluri' => $principalUri,
1570
+            'uri'          => $uri,
1571
+            'source'       => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1572
+            'lastmodified' => time(),
1573
+        ];
1574
+
1575
+        $propertiesBoolean = ['striptodos', 'stripalarms', 'stripattachments'];
1576
+
1577
+        foreach($this->subscriptionPropertyMap as $xmlName=>$dbName) {
1578
+            if (array_key_exists($xmlName, $properties)) {
1579
+                    $values[$dbName] = $properties[$xmlName];
1580
+                    if (in_array($dbName, $propertiesBoolean)) {
1581
+                        $values[$dbName] = true;
1582
+                }
1583
+            }
1584
+        }
1585
+
1586
+        $valuesToInsert = array();
1587
+
1588
+        $query = $this->db->getQueryBuilder();
1589
+
1590
+        foreach (array_keys($values) as $name) {
1591
+            $valuesToInsert[$name] = $query->createNamedParameter($values[$name]);
1592
+        }
1593
+
1594
+        $query->insert('calendarsubscriptions')
1595
+            ->values($valuesToInsert)
1596
+            ->execute();
1597
+
1598
+        return $this->db->lastInsertId('*PREFIX*calendarsubscriptions');
1599
+    }
1600
+
1601
+    /**
1602
+     * Updates a subscription
1603
+     *
1604
+     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1605
+     * To do the actual updates, you must tell this object which properties
1606
+     * you're going to process with the handle() method.
1607
+     *
1608
+     * Calling the handle method is like telling the PropPatch object "I
1609
+     * promise I can handle updating this property".
1610
+     *
1611
+     * Read the PropPatch documentation for more info and examples.
1612
+     *
1613
+     * @param mixed $subscriptionId
1614
+     * @param PropPatch $propPatch
1615
+     * @return void
1616
+     */
1617
+    function updateSubscription($subscriptionId, PropPatch $propPatch) {
1618
+        $supportedProperties = array_keys($this->subscriptionPropertyMap);
1619
+        $supportedProperties[] = '{http://calendarserver.org/ns/}source';
1620
+
1621
+        $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
1622
+
1623
+            $newValues = [];
1624
+
1625
+            foreach($mutations as $propertyName=>$propertyValue) {
1626
+                if ($propertyName === '{http://calendarserver.org/ns/}source') {
1627
+                    $newValues['source'] = $propertyValue->getHref();
1628
+                } else {
1629
+                    $fieldName = $this->subscriptionPropertyMap[$propertyName];
1630
+                    $newValues[$fieldName] = $propertyValue;
1631
+                }
1632
+            }
1633
+
1634
+            $query = $this->db->getQueryBuilder();
1635
+            $query->update('calendarsubscriptions')
1636
+                ->set('lastmodified', $query->createNamedParameter(time()));
1637
+            foreach($newValues as $fieldName=>$value) {
1638
+                $query->set($fieldName, $query->createNamedParameter($value));
1639
+            }
1640
+            $query->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId)))
1641
+                ->execute();
1642
+
1643
+            return true;
1644
+
1645
+        });
1646
+    }
1647
+
1648
+    /**
1649
+     * Deletes a subscription.
1650
+     *
1651
+     * @param mixed $subscriptionId
1652
+     * @return void
1653
+     */
1654
+    function deleteSubscription($subscriptionId) {
1655
+        $query = $this->db->getQueryBuilder();
1656
+        $query->delete('calendarsubscriptions')
1657
+            ->where($query->expr()->eq('id', $query->createNamedParameter($subscriptionId)))
1658
+            ->execute();
1659
+    }
1660
+
1661
+    /**
1662
+     * Returns a single scheduling object for the inbox collection.
1663
+     *
1664
+     * The returned array should contain the following elements:
1665
+     *   * uri - A unique basename for the object. This will be used to
1666
+     *           construct a full uri.
1667
+     *   * calendardata - The iCalendar object
1668
+     *   * lastmodified - The last modification date. Can be an int for a unix
1669
+     *                    timestamp, or a PHP DateTime object.
1670
+     *   * etag - A unique token that must change if the object changed.
1671
+     *   * size - The size of the object, in bytes.
1672
+     *
1673
+     * @param string $principalUri
1674
+     * @param string $objectUri
1675
+     * @return array
1676
+     */
1677
+    function getSchedulingObject($principalUri, $objectUri) {
1678
+        $query = $this->db->getQueryBuilder();
1679
+        $stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size'])
1680
+            ->from('schedulingobjects')
1681
+            ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1682
+            ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1683
+            ->execute();
1684
+
1685
+        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
1686
+
1687
+        if(!$row) {
1688
+            return null;
1689
+        }
1690
+
1691
+        return [
1692
+                'uri'          => $row['uri'],
1693
+                'calendardata' => $row['calendardata'],
1694
+                'lastmodified' => $row['lastmodified'],
1695
+                'etag'         => '"' . $row['etag'] . '"',
1696
+                'size'         => (int)$row['size'],
1697
+        ];
1698
+    }
1699
+
1700
+    /**
1701
+     * Returns all scheduling objects for the inbox collection.
1702
+     *
1703
+     * These objects should be returned as an array. Every item in the array
1704
+     * should follow the same structure as returned from getSchedulingObject.
1705
+     *
1706
+     * The main difference is that 'calendardata' is optional.
1707
+     *
1708
+     * @param string $principalUri
1709
+     * @return array
1710
+     */
1711
+    function getSchedulingObjects($principalUri) {
1712
+        $query = $this->db->getQueryBuilder();
1713
+        $stmt = $query->select(['uri', 'calendardata', 'lastmodified', 'etag', 'size'])
1714
+                ->from('schedulingobjects')
1715
+                ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1716
+                ->execute();
1717
+
1718
+        $result = [];
1719
+        foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1720
+            $result[] = [
1721
+                    'calendardata' => $row['calendardata'],
1722
+                    'uri'          => $row['uri'],
1723
+                    'lastmodified' => $row['lastmodified'],
1724
+                    'etag'         => '"' . $row['etag'] . '"',
1725
+                    'size'         => (int)$row['size'],
1726
+            ];
1727
+        }
1728
+
1729
+        return $result;
1730
+    }
1731
+
1732
+    /**
1733
+     * Deletes a scheduling object from the inbox collection.
1734
+     *
1735
+     * @param string $principalUri
1736
+     * @param string $objectUri
1737
+     * @return void
1738
+     */
1739
+    function deleteSchedulingObject($principalUri, $objectUri) {
1740
+        $query = $this->db->getQueryBuilder();
1741
+        $query->delete('schedulingobjects')
1742
+                ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
1743
+                ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($objectUri)))
1744
+                ->execute();
1745
+    }
1746
+
1747
+    /**
1748
+     * Creates a new scheduling object. This should land in a users' inbox.
1749
+     *
1750
+     * @param string $principalUri
1751
+     * @param string $objectUri
1752
+     * @param string $objectData
1753
+     * @return void
1754
+     */
1755
+    function createSchedulingObject($principalUri, $objectUri, $objectData) {
1756
+        $query = $this->db->getQueryBuilder();
1757
+        $query->insert('schedulingobjects')
1758
+            ->values([
1759
+                'principaluri' => $query->createNamedParameter($principalUri),
1760
+                'calendardata' => $query->createNamedParameter($objectData),
1761
+                'uri' => $query->createNamedParameter($objectUri),
1762
+                'lastmodified' => $query->createNamedParameter(time()),
1763
+                'etag' => $query->createNamedParameter(md5($objectData)),
1764
+                'size' => $query->createNamedParameter(strlen($objectData))
1765
+            ])
1766
+            ->execute();
1767
+    }
1768
+
1769
+    /**
1770
+     * Adds a change record to the calendarchanges table.
1771
+     *
1772
+     * @param mixed $calendarId
1773
+     * @param string $objectUri
1774
+     * @param int $operation 1 = add, 2 = modify, 3 = delete.
1775
+     * @return void
1776
+     */
1777
+    protected function addChange($calendarId, $objectUri, $operation) {
1778
+
1779
+        $stmt = $this->db->prepare('INSERT INTO `*PREFIX*calendarchanges` (`uri`, `synctoken`, `calendarid`, `operation`) SELECT ?, `synctoken`, ?, ? FROM `*PREFIX*calendars` WHERE `id` = ?');
1780
+        $stmt->execute([
1781
+            $objectUri,
1782
+            $calendarId,
1783
+            $operation,
1784
+            $calendarId
1785
+        ]);
1786
+        $stmt = $this->db->prepare('UPDATE `*PREFIX*calendars` SET `synctoken` = `synctoken` + 1 WHERE `id` = ?');
1787
+        $stmt->execute([
1788
+            $calendarId
1789
+        ]);
1790
+
1791
+    }
1792
+
1793
+    /**
1794
+     * Parses some information from calendar objects, used for optimized
1795
+     * calendar-queries.
1796
+     *
1797
+     * Returns an array with the following keys:
1798
+     *   * etag - An md5 checksum of the object without the quotes.
1799
+     *   * size - Size of the object in bytes
1800
+     *   * componentType - VEVENT, VTODO or VJOURNAL
1801
+     *   * firstOccurence
1802
+     *   * lastOccurence
1803
+     *   * uid - value of the UID property
1804
+     *
1805
+     * @param string $calendarData
1806
+     * @return array
1807
+     */
1808
+    public function getDenormalizedData($calendarData) {
1809
+
1810
+        $vObject = Reader::read($calendarData);
1811
+        $componentType = null;
1812
+        $component = null;
1813
+        $firstOccurrence = null;
1814
+        $lastOccurrence = null;
1815
+        $uid = null;
1816
+        $classification = self::CLASSIFICATION_PUBLIC;
1817
+        foreach($vObject->getComponents() as $component) {
1818
+            if ($component->name!=='VTIMEZONE') {
1819
+                $componentType = $component->name;
1820
+                $uid = (string)$component->UID;
1821
+                break;
1822
+            }
1823
+        }
1824
+        if (!$componentType) {
1825
+            throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
1826
+        }
1827
+        if ($componentType === 'VEVENT' && $component->DTSTART) {
1828
+            $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
1829
+            // Finding the last occurrence is a bit harder
1830
+            if (!isset($component->RRULE)) {
1831
+                if (isset($component->DTEND)) {
1832
+                    $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
1833
+                } elseif (isset($component->DURATION)) {
1834
+                    $endDate = clone $component->DTSTART->getDateTime();
1835
+                    $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
1836
+                    $lastOccurrence = $endDate->getTimeStamp();
1837
+                } elseif (!$component->DTSTART->hasTime()) {
1838
+                    $endDate = clone $component->DTSTART->getDateTime();
1839
+                    $endDate->modify('+1 day');
1840
+                    $lastOccurrence = $endDate->getTimeStamp();
1841
+                } else {
1842
+                    $lastOccurrence = $firstOccurrence;
1843
+                }
1844
+            } else {
1845
+                $it = new EventIterator($vObject, (string)$component->UID);
1846
+                $maxDate = new \DateTime(self::MAX_DATE);
1847
+                if ($it->isInfinite()) {
1848
+                    $lastOccurrence = $maxDate->getTimestamp();
1849
+                } else {
1850
+                    $end = $it->getDtEnd();
1851
+                    while($it->valid() && $end < $maxDate) {
1852
+                        $end = $it->getDtEnd();
1853
+                        $it->next();
1854
+
1855
+                    }
1856
+                    $lastOccurrence = $end->getTimestamp();
1857
+                }
1858
+
1859
+            }
1860
+        }
1861
+
1862
+        if ($component->CLASS) {
1863
+            $classification = CalDavBackend::CLASSIFICATION_PRIVATE;
1864
+            switch ($component->CLASS->getValue()) {
1865
+                case 'PUBLIC':
1866
+                    $classification = CalDavBackend::CLASSIFICATION_PUBLIC;
1867
+                    break;
1868
+                case 'CONFIDENTIAL':
1869
+                    $classification = CalDavBackend::CLASSIFICATION_CONFIDENTIAL;
1870
+                    break;
1871
+            }
1872
+        }
1873
+        return [
1874
+            'etag' => md5($calendarData),
1875
+            'size' => strlen($calendarData),
1876
+            'componentType' => $componentType,
1877
+            'firstOccurence' => is_null($firstOccurrence) ? null : max(0, $firstOccurrence),
1878
+            'lastOccurence'  => $lastOccurrence,
1879
+            'uid' => $uid,
1880
+            'classification' => $classification
1881
+        ];
1882
+
1883
+    }
1884
+
1885
+    private function readBlob($cardData) {
1886
+        if (is_resource($cardData)) {
1887
+            return stream_get_contents($cardData);
1888
+        }
1889
+
1890
+        return $cardData;
1891
+    }
1892
+
1893
+    /**
1894
+     * @param IShareable $shareable
1895
+     * @param array $add
1896
+     * @param array $remove
1897
+     */
1898
+    public function updateShares($shareable, $add, $remove) {
1899
+        $calendarId = $shareable->getResourceId();
1900
+        $this->dispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::updateShares', new GenericEvent(
1901
+            '\OCA\DAV\CalDAV\CalDavBackend::updateShares',
1902
+            [
1903
+                'calendarId' => $calendarId,
1904
+                'calendarData' => $this->getCalendarById($calendarId),
1905
+                'shares' => $this->getShares($calendarId),
1906
+                'add' => $add,
1907
+                'remove' => $remove,
1908
+            ]));
1909
+        $this->sharingBackend->updateShares($shareable, $add, $remove);
1910
+    }
1911
+
1912
+    /**
1913
+     * @param int $resourceId
1914
+     * @return array
1915
+     */
1916
+    public function getShares($resourceId) {
1917
+        return $this->sharingBackend->getShares($resourceId);
1918
+    }
1919
+
1920
+    /**
1921
+     * @param boolean $value
1922
+     * @param \OCA\DAV\CalDAV\Calendar $calendar
1923
+     * @return string|null
1924
+     */
1925
+    public function setPublishStatus($value, $calendar) {
1926
+        $query = $this->db->getQueryBuilder();
1927
+        if ($value) {
1928
+            $publicUri = $this->random->generate(16, ISecureRandom::CHAR_HUMAN_READABLE);
1929
+            $query->insert('dav_shares')
1930
+                ->values([
1931
+                    'principaluri' => $query->createNamedParameter($calendar->getPrincipalURI()),
1932
+                    'type' => $query->createNamedParameter('calendar'),
1933
+                    'access' => $query->createNamedParameter(self::ACCESS_PUBLIC),
1934
+                    'resourceid' => $query->createNamedParameter($calendar->getResourceId()),
1935
+                    'publicuri' => $query->createNamedParameter($publicUri)
1936
+                ]);
1937
+            $query->execute();
1938
+            return $publicUri;
1939
+        }
1940
+        $query->delete('dav_shares')
1941
+            ->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId())))
1942
+            ->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)));
1943
+        $query->execute();
1944
+        return null;
1945
+    }
1946
+
1947
+    /**
1948
+     * @param \OCA\DAV\CalDAV\Calendar $calendar
1949
+     * @return mixed
1950
+     */
1951
+    public function getPublishStatus($calendar) {
1952
+        $query = $this->db->getQueryBuilder();
1953
+        $result = $query->select('publicuri')
1954
+            ->from('dav_shares')
1955
+            ->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId())))
1956
+            ->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)))
1957
+            ->execute();
1958
+
1959
+        $row = $result->fetch();
1960
+        $result->closeCursor();
1961
+        return $row ? reset($row) : false;
1962
+    }
1963
+
1964
+    /**
1965
+     * @param int $resourceId
1966
+     * @param array $acl
1967
+     * @return array
1968
+     */
1969
+    public function applyShareAcl($resourceId, $acl) {
1970
+        return $this->sharingBackend->applyShareAcl($resourceId, $acl);
1971
+    }
1972
+
1973
+
1974
+
1975
+    /**
1976
+     * update properties table
1977
+     *
1978
+     * @param int $calendarId
1979
+     * @param string $objectUri
1980
+     * @param string $calendarData
1981
+     */
1982
+    public function updateProperties($calendarId, $objectUri, $calendarData) {
1983
+        $objectId = $this->getCalendarObjectId($calendarId, $objectUri);
1984
+
1985
+        try {
1986
+            $vCalendar = $this->readCalendarData($calendarData);
1987
+        } catch (\Exception $ex) {
1988
+            return;
1989
+        }
1990
+
1991
+        $this->purgeProperties($calendarId, $objectId);
1992
+
1993
+        $query = $this->db->getQueryBuilder();
1994
+        $query->insert($this->dbObjectPropertiesTable)
1995
+            ->values(
1996
+                [
1997
+                    'calendarid' => $query->createNamedParameter($calendarId),
1998
+                    'objectid' => $query->createNamedParameter($objectId),
1999
+                    'name' => $query->createParameter('name'),
2000
+                    'parameter' => $query->createParameter('parameter'),
2001
+                    'value' => $query->createParameter('value'),
2002
+                ]
2003
+            );
2004
+
2005
+        $indexComponents = ['VEVENT', 'VJOURNAL', 'VTODO'];
2006
+        foreach ($vCalendar->getComponents() as $component) {
2007
+            if (!in_array($component->name, $indexComponents)) {
2008
+                continue;
2009
+            }
2010
+
2011
+            foreach ($component->children() as $property) {
2012
+                if (in_array($property->name, self::$indexProperties)) {
2013
+                    $value = $property->getValue();
2014
+                    // is this a shitty db?
2015
+                    if (!$this->db->supports4ByteText()) {
2016
+                        $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value);
2017
+                    }
2018
+                    $value = substr($value, 0, 254);
2019
+
2020
+                    $query->setParameter('name', $property->name);
2021
+                    $query->setParameter('parameter', null);
2022
+                    $query->setParameter('value', $value);
2023
+                    $query->execute();
2024
+                }
2025
+
2026
+                if (in_array($property->name, array_keys(self::$indexParameters))) {
2027
+                    $parameters = $property->parameters();
2028
+                    $indexedParametersForProperty = self::$indexParameters[$property->name];
2029
+
2030
+                    foreach ($parameters as $key => $value) {
2031
+                        if (in_array($key, $indexedParametersForProperty)) {
2032
+                            // is this a shitty db?
2033
+                            if ($this->db->supports4ByteText()) {
2034
+                                $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value);
2035
+                            }
2036
+                            $value = substr($value, 0, 254);
2037
+
2038
+                            $query->setParameter('name', $property->name);
2039
+                            $query->setParameter('parameter', substr($key, 0, 254));
2040
+                            $query->setParameter('value', substr($value, 0, 254));
2041
+                            $query->execute();
2042
+                        }
2043
+                    }
2044
+                }
2045
+            }
2046
+        }
2047
+    }
2048
+
2049
+    /**
2050
+     * read VCalendar data into a VCalendar object
2051
+     *
2052
+     * @param string $objectData
2053
+     * @return VCalendar
2054
+     */
2055
+    protected function readCalendarData($objectData) {
2056
+        return Reader::read($objectData);
2057
+    }
2058
+
2059
+    /**
2060
+     * delete all properties from a given calendar object
2061
+     *
2062
+     * @param int $calendarId
2063
+     * @param int $objectId
2064
+     */
2065
+    protected function purgeProperties($calendarId, $objectId) {
2066
+        $query = $this->db->getQueryBuilder();
2067
+        $query->delete($this->dbObjectPropertiesTable)
2068
+            ->where($query->expr()->eq('objectid', $query->createNamedParameter($objectId)))
2069
+            ->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
2070
+        $query->execute();
2071
+    }
2072
+
2073
+    /**
2074
+     * get ID from a given calendar object
2075
+     *
2076
+     * @param int $calendarId
2077
+     * @param string $uri
2078
+     * @return int
2079
+     */
2080
+    protected function getCalendarObjectId($calendarId, $uri) {
2081
+        $query = $this->db->getQueryBuilder();
2082
+        $query->select('id')->from('calendarobjects')
2083
+            ->where($query->expr()->eq('uri', $query->createNamedParameter($uri)))
2084
+            ->andWhere($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)));
2085
+
2086
+        $result = $query->execute();
2087
+        $objectIds = $result->fetch();
2088
+        $result->closeCursor();
2089
+
2090
+        if (!isset($objectIds['id'])) {
2091
+            throw new \InvalidArgumentException('Calendarobject does not exists: ' . $uri);
2092
+        }
2093
+
2094
+        return (int)$objectIds['id'];
2095
+    }
2096
+
2097
+    private function convertPrincipal($principalUri, $toV2) {
2098
+        if ($this->principalBackend->getPrincipalPrefix() === 'principals') {
2099
+            list(, $name) = URLUtil::splitPath($principalUri);
2100
+            if ($toV2 === true) {
2101
+                return "principals/users/$name";
2102
+            }
2103
+            return "principals/$name";
2104
+        }
2105
+        return $principalUri;
2106
+    }
2107
+
2108
+    private function addOwnerPrincipal(&$calendarInfo) {
2109
+        $ownerPrincipalKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal';
2110
+        $displaynameKey = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}owner-displayname';
2111
+        if (isset($calendarInfo[$ownerPrincipalKey])) {
2112
+            $uri = $calendarInfo[$ownerPrincipalKey];
2113
+        } else {
2114
+            $uri = $calendarInfo['principaluri'];
2115
+        }
2116
+
2117
+        $principalInformation = $this->principalBackend->getPrincipalByPath($uri);
2118
+        if (isset($principalInformation['{DAV:}displayname'])) {
2119
+            $calendarInfo[$displaynameKey] = $principalInformation['{DAV:}displayname'];
2120
+        }
2121
+    }
2122 2122
 }
Please login to merge, or discard this patch.
lib/private/Share20/Manager.php 1 patch
Indentation   +1360 added lines, -1360 removed lines patch added patch discarded remove patch
@@ -56,1388 +56,1388 @@
 block discarded – undo
56 56
  */
57 57
 class Manager implements IManager {
58 58
 
59
-	/** @var IProviderFactory */
60
-	private $factory;
61
-	/** @var ILogger */
62
-	private $logger;
63
-	/** @var IConfig */
64
-	private $config;
65
-	/** @var ISecureRandom */
66
-	private $secureRandom;
67
-	/** @var IHasher */
68
-	private $hasher;
69
-	/** @var IMountManager */
70
-	private $mountManager;
71
-	/** @var IGroupManager */
72
-	private $groupManager;
73
-	/** @var IL10N */
74
-	private $l;
75
-	/** @var IUserManager */
76
-	private $userManager;
77
-	/** @var IRootFolder */
78
-	private $rootFolder;
79
-	/** @var CappedMemoryCache */
80
-	private $sharingDisabledForUsersCache;
81
-	/** @var EventDispatcher */
82
-	private $eventDispatcher;
83
-	/** @var LegacyHooks */
84
-	private $legacyHooks;
85
-
86
-
87
-	/**
88
-	 * Manager constructor.
89
-	 *
90
-	 * @param ILogger $logger
91
-	 * @param IConfig $config
92
-	 * @param ISecureRandom $secureRandom
93
-	 * @param IHasher $hasher
94
-	 * @param IMountManager $mountManager
95
-	 * @param IGroupManager $groupManager
96
-	 * @param IL10N $l
97
-	 * @param IProviderFactory $factory
98
-	 * @param IUserManager $userManager
99
-	 * @param IRootFolder $rootFolder
100
-	 * @param EventDispatcher $eventDispatcher
101
-	 */
102
-	public function __construct(
103
-			ILogger $logger,
104
-			IConfig $config,
105
-			ISecureRandom $secureRandom,
106
-			IHasher $hasher,
107
-			IMountManager $mountManager,
108
-			IGroupManager $groupManager,
109
-			IL10N $l,
110
-			IProviderFactory $factory,
111
-			IUserManager $userManager,
112
-			IRootFolder $rootFolder,
113
-			EventDispatcher $eventDispatcher
114
-	) {
115
-		$this->logger = $logger;
116
-		$this->config = $config;
117
-		$this->secureRandom = $secureRandom;
118
-		$this->hasher = $hasher;
119
-		$this->mountManager = $mountManager;
120
-		$this->groupManager = $groupManager;
121
-		$this->l = $l;
122
-		$this->factory = $factory;
123
-		$this->userManager = $userManager;
124
-		$this->rootFolder = $rootFolder;
125
-		$this->eventDispatcher = $eventDispatcher;
126
-		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
127
-		$this->legacyHooks = new LegacyHooks($this->eventDispatcher);
128
-	}
129
-
130
-	/**
131
-	 * Convert from a full share id to a tuple (providerId, shareId)
132
-	 *
133
-	 * @param string $id
134
-	 * @return string[]
135
-	 */
136
-	private function splitFullId($id) {
137
-		return explode(':', $id, 2);
138
-	}
139
-
140
-	/**
141
-	 * Verify if a password meets all requirements
142
-	 *
143
-	 * @param string $password
144
-	 * @throws \Exception
145
-	 */
146
-	protected function verifyPassword($password) {
147
-		if ($password === null) {
148
-			// No password is set, check if this is allowed.
149
-			if ($this->shareApiLinkEnforcePassword()) {
150
-				throw new \InvalidArgumentException('Passwords are enforced for link shares');
151
-			}
152
-
153
-			return;
154
-		}
155
-
156
-		// Let others verify the password
157
-		try {
158
-			$event = new GenericEvent($password);
159
-			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
160
-		} catch (HintException $e) {
161
-			throw new \Exception($e->getHint());
162
-		}
163
-	}
164
-
165
-	/**
166
-	 * Check for generic requirements before creating a share
167
-	 *
168
-	 * @param \OCP\Share\IShare $share
169
-	 * @throws \InvalidArgumentException
170
-	 * @throws GenericShareException
171
-	 *
172
-	 * @suppress PhanUndeclaredClassMethod
173
-	 */
174
-	protected function generalCreateChecks(\OCP\Share\IShare $share) {
175
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
176
-			// We expect a valid user as sharedWith for user shares
177
-			if (!$this->userManager->userExists($share->getSharedWith())) {
178
-				throw new \InvalidArgumentException('SharedWith is not a valid user');
179
-			}
180
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
181
-			// We expect a valid group as sharedWith for group shares
182
-			if (!$this->groupManager->groupExists($share->getSharedWith())) {
183
-				throw new \InvalidArgumentException('SharedWith is not a valid group');
184
-			}
185
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
186
-			if ($share->getSharedWith() !== null) {
187
-				throw new \InvalidArgumentException('SharedWith should be empty');
188
-			}
189
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
190
-			if ($share->getSharedWith() === null) {
191
-				throw new \InvalidArgumentException('SharedWith should not be empty');
192
-			}
193
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
194
-			if ($share->getSharedWith() === null) {
195
-				throw new \InvalidArgumentException('SharedWith should not be empty');
196
-			}
197
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
199
-			if ($circle === null) {
200
-				throw new \InvalidArgumentException('SharedWith is not a valid circle');
201
-			}
202
-		} else {
203
-			// We can't handle other types yet
204
-			throw new \InvalidArgumentException('unknown share type');
205
-		}
206
-
207
-		// Verify the initiator of the share is set
208
-		if ($share->getSharedBy() === null) {
209
-			throw new \InvalidArgumentException('SharedBy should be set');
210
-		}
211
-
212
-		// Cannot share with yourself
213
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
214
-			$share->getSharedWith() === $share->getSharedBy()) {
215
-			throw new \InvalidArgumentException('Can’t share with yourself');
216
-		}
217
-
218
-		// The path should be set
219
-		if ($share->getNode() === null) {
220
-			throw new \InvalidArgumentException('Path should be set');
221
-		}
222
-
223
-		// And it should be a file or a folder
224
-		if (!($share->getNode() instanceof \OCP\Files\File) &&
225
-				!($share->getNode() instanceof \OCP\Files\Folder)) {
226
-			throw new \InvalidArgumentException('Path should be either a file or a folder');
227
-		}
228
-
229
-		// And you can't share your rootfolder
230
-		if ($this->userManager->userExists($share->getSharedBy())) {
231
-			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
232
-		} else {
233
-			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
234
-		}
235
-		if ($sharedPath === $share->getNode()->getPath()) {
236
-			throw new \InvalidArgumentException('You can’t share your root folder');
237
-		}
238
-
239
-		// Check if we actually have share permissions
240
-		if (!$share->getNode()->isShareable()) {
241
-			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
242
-			throw new GenericShareException($message_t, $message_t, 404);
243
-		}
244
-
245
-		// Permissions should be set
246
-		if ($share->getPermissions() === null) {
247
-			throw new \InvalidArgumentException('A share requires permissions');
248
-		}
249
-
250
-		/*
59
+    /** @var IProviderFactory */
60
+    private $factory;
61
+    /** @var ILogger */
62
+    private $logger;
63
+    /** @var IConfig */
64
+    private $config;
65
+    /** @var ISecureRandom */
66
+    private $secureRandom;
67
+    /** @var IHasher */
68
+    private $hasher;
69
+    /** @var IMountManager */
70
+    private $mountManager;
71
+    /** @var IGroupManager */
72
+    private $groupManager;
73
+    /** @var IL10N */
74
+    private $l;
75
+    /** @var IUserManager */
76
+    private $userManager;
77
+    /** @var IRootFolder */
78
+    private $rootFolder;
79
+    /** @var CappedMemoryCache */
80
+    private $sharingDisabledForUsersCache;
81
+    /** @var EventDispatcher */
82
+    private $eventDispatcher;
83
+    /** @var LegacyHooks */
84
+    private $legacyHooks;
85
+
86
+
87
+    /**
88
+     * Manager constructor.
89
+     *
90
+     * @param ILogger $logger
91
+     * @param IConfig $config
92
+     * @param ISecureRandom $secureRandom
93
+     * @param IHasher $hasher
94
+     * @param IMountManager $mountManager
95
+     * @param IGroupManager $groupManager
96
+     * @param IL10N $l
97
+     * @param IProviderFactory $factory
98
+     * @param IUserManager $userManager
99
+     * @param IRootFolder $rootFolder
100
+     * @param EventDispatcher $eventDispatcher
101
+     */
102
+    public function __construct(
103
+            ILogger $logger,
104
+            IConfig $config,
105
+            ISecureRandom $secureRandom,
106
+            IHasher $hasher,
107
+            IMountManager $mountManager,
108
+            IGroupManager $groupManager,
109
+            IL10N $l,
110
+            IProviderFactory $factory,
111
+            IUserManager $userManager,
112
+            IRootFolder $rootFolder,
113
+            EventDispatcher $eventDispatcher
114
+    ) {
115
+        $this->logger = $logger;
116
+        $this->config = $config;
117
+        $this->secureRandom = $secureRandom;
118
+        $this->hasher = $hasher;
119
+        $this->mountManager = $mountManager;
120
+        $this->groupManager = $groupManager;
121
+        $this->l = $l;
122
+        $this->factory = $factory;
123
+        $this->userManager = $userManager;
124
+        $this->rootFolder = $rootFolder;
125
+        $this->eventDispatcher = $eventDispatcher;
126
+        $this->sharingDisabledForUsersCache = new CappedMemoryCache();
127
+        $this->legacyHooks = new LegacyHooks($this->eventDispatcher);
128
+    }
129
+
130
+    /**
131
+     * Convert from a full share id to a tuple (providerId, shareId)
132
+     *
133
+     * @param string $id
134
+     * @return string[]
135
+     */
136
+    private function splitFullId($id) {
137
+        return explode(':', $id, 2);
138
+    }
139
+
140
+    /**
141
+     * Verify if a password meets all requirements
142
+     *
143
+     * @param string $password
144
+     * @throws \Exception
145
+     */
146
+    protected function verifyPassword($password) {
147
+        if ($password === null) {
148
+            // No password is set, check if this is allowed.
149
+            if ($this->shareApiLinkEnforcePassword()) {
150
+                throw new \InvalidArgumentException('Passwords are enforced for link shares');
151
+            }
152
+
153
+            return;
154
+        }
155
+
156
+        // Let others verify the password
157
+        try {
158
+            $event = new GenericEvent($password);
159
+            $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
160
+        } catch (HintException $e) {
161
+            throw new \Exception($e->getHint());
162
+        }
163
+    }
164
+
165
+    /**
166
+     * Check for generic requirements before creating a share
167
+     *
168
+     * @param \OCP\Share\IShare $share
169
+     * @throws \InvalidArgumentException
170
+     * @throws GenericShareException
171
+     *
172
+     * @suppress PhanUndeclaredClassMethod
173
+     */
174
+    protected function generalCreateChecks(\OCP\Share\IShare $share) {
175
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
176
+            // We expect a valid user as sharedWith for user shares
177
+            if (!$this->userManager->userExists($share->getSharedWith())) {
178
+                throw new \InvalidArgumentException('SharedWith is not a valid user');
179
+            }
180
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
181
+            // We expect a valid group as sharedWith for group shares
182
+            if (!$this->groupManager->groupExists($share->getSharedWith())) {
183
+                throw new \InvalidArgumentException('SharedWith is not a valid group');
184
+            }
185
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
186
+            if ($share->getSharedWith() !== null) {
187
+                throw new \InvalidArgumentException('SharedWith should be empty');
188
+            }
189
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
190
+            if ($share->getSharedWith() === null) {
191
+                throw new \InvalidArgumentException('SharedWith should not be empty');
192
+            }
193
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
194
+            if ($share->getSharedWith() === null) {
195
+                throw new \InvalidArgumentException('SharedWith should not be empty');
196
+            }
197
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith());
199
+            if ($circle === null) {
200
+                throw new \InvalidArgumentException('SharedWith is not a valid circle');
201
+            }
202
+        } else {
203
+            // We can't handle other types yet
204
+            throw new \InvalidArgumentException('unknown share type');
205
+        }
206
+
207
+        // Verify the initiator of the share is set
208
+        if ($share->getSharedBy() === null) {
209
+            throw new \InvalidArgumentException('SharedBy should be set');
210
+        }
211
+
212
+        // Cannot share with yourself
213
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
214
+            $share->getSharedWith() === $share->getSharedBy()) {
215
+            throw new \InvalidArgumentException('Can’t share with yourself');
216
+        }
217
+
218
+        // The path should be set
219
+        if ($share->getNode() === null) {
220
+            throw new \InvalidArgumentException('Path should be set');
221
+        }
222
+
223
+        // And it should be a file or a folder
224
+        if (!($share->getNode() instanceof \OCP\Files\File) &&
225
+                !($share->getNode() instanceof \OCP\Files\Folder)) {
226
+            throw new \InvalidArgumentException('Path should be either a file or a folder');
227
+        }
228
+
229
+        // And you can't share your rootfolder
230
+        if ($this->userManager->userExists($share->getSharedBy())) {
231
+            $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
232
+        } else {
233
+            $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
234
+        }
235
+        if ($sharedPath === $share->getNode()->getPath()) {
236
+            throw new \InvalidArgumentException('You can’t share your root folder');
237
+        }
238
+
239
+        // Check if we actually have share permissions
240
+        if (!$share->getNode()->isShareable()) {
241
+            $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
242
+            throw new GenericShareException($message_t, $message_t, 404);
243
+        }
244
+
245
+        // Permissions should be set
246
+        if ($share->getPermissions() === null) {
247
+            throw new \InvalidArgumentException('A share requires permissions');
248
+        }
249
+
250
+        /*
251 251
 		 * Quick fix for #23536
252 252
 		 * Non moveable mount points do not have update and delete permissions
253 253
 		 * while we 'most likely' do have that on the storage.
254 254
 		 */
255
-		$permissions = $share->getNode()->getPermissions();
256
-		$mount = $share->getNode()->getMountPoint();
257
-		if (!($mount instanceof MoveableMount)) {
258
-			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
259
-		}
260
-
261
-		// Check that we do not share with more permissions than we have
262
-		if ($share->getPermissions() & ~$permissions) {
263
-			$message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
264
-			throw new GenericShareException($message_t, $message_t, 404);
265
-		}
266
-
267
-
268
-		// Check that read permissions are always set
269
-		// Link shares are allowed to have no read permissions to allow upload to hidden folders
270
-		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
271
-			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
272
-		if (!$noReadPermissionRequired &&
273
-			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
274
-			throw new \InvalidArgumentException('Shares need at least read permissions');
275
-		}
276
-
277
-		if ($share->getNode() instanceof \OCP\Files\File) {
278
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
279
-				$message_t = $this->l->t('Files can’t be shared with delete permissions');
280
-				throw new GenericShareException($message_t);
281
-			}
282
-			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
283
-				$message_t = $this->l->t('Files can’t be shared with create permissions');
284
-				throw new GenericShareException($message_t);
285
-			}
286
-		}
287
-	}
288
-
289
-	/**
290
-	 * Validate if the expiration date fits the system settings
291
-	 *
292
-	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
293
-	 * @return \OCP\Share\IShare The modified share object
294
-	 * @throws GenericShareException
295
-	 * @throws \InvalidArgumentException
296
-	 * @throws \Exception
297
-	 */
298
-	protected function validateExpirationDate(\OCP\Share\IShare $share) {
299
-
300
-		$expirationDate = $share->getExpirationDate();
301
-
302
-		if ($expirationDate !== null) {
303
-			//Make sure the expiration date is a date
304
-			$expirationDate->setTime(0, 0, 0);
305
-
306
-			$date = new \DateTime();
307
-			$date->setTime(0, 0, 0);
308
-			if ($date >= $expirationDate) {
309
-				$message = $this->l->t('Expiration date is in the past');
310
-				throw new GenericShareException($message, $message, 404);
311
-			}
312
-		}
313
-
314
-		// If expiredate is empty set a default one if there is a default
315
-		$fullId = null;
316
-		try {
317
-			$fullId = $share->getFullId();
318
-		} catch (\UnexpectedValueException $e) {
319
-			// This is a new share
320
-		}
321
-
322
-		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
323
-			$expirationDate = new \DateTime();
324
-			$expirationDate->setTime(0,0,0);
325
-			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
326
-		}
327
-
328
-		// If we enforce the expiration date check that is does not exceed
329
-		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
330
-			if ($expirationDate === null) {
331
-				throw new \InvalidArgumentException('Expiration date is enforced');
332
-			}
333
-
334
-			$date = new \DateTime();
335
-			$date->setTime(0, 0, 0);
336
-			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
337
-			if ($date < $expirationDate) {
338
-				$message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
339
-				throw new GenericShareException($message, $message, 404);
340
-			}
341
-		}
342
-
343
-		$accepted = true;
344
-		$message = '';
345
-		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
346
-			'expirationDate' => &$expirationDate,
347
-			'accepted' => &$accepted,
348
-			'message' => &$message,
349
-			'passwordSet' => $share->getPassword() !== null,
350
-		]);
351
-
352
-		if (!$accepted) {
353
-			throw new \Exception($message);
354
-		}
355
-
356
-		$share->setExpirationDate($expirationDate);
357
-
358
-		return $share;
359
-	}
360
-
361
-	/**
362
-	 * Check for pre share requirements for user shares
363
-	 *
364
-	 * @param \OCP\Share\IShare $share
365
-	 * @throws \Exception
366
-	 */
367
-	protected function userCreateChecks(\OCP\Share\IShare $share) {
368
-		// Check if we can share with group members only
369
-		if ($this->shareWithGroupMembersOnly()) {
370
-			$sharedBy = $this->userManager->get($share->getSharedBy());
371
-			$sharedWith = $this->userManager->get($share->getSharedWith());
372
-			// Verify we can share with this user
373
-			$groups = array_intersect(
374
-					$this->groupManager->getUserGroupIds($sharedBy),
375
-					$this->groupManager->getUserGroupIds($sharedWith)
376
-			);
377
-			if (empty($groups)) {
378
-				throw new \Exception('Sharing is only allowed with group members');
379
-			}
380
-		}
381
-
382
-		/*
255
+        $permissions = $share->getNode()->getPermissions();
256
+        $mount = $share->getNode()->getMountPoint();
257
+        if (!($mount instanceof MoveableMount)) {
258
+            $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
259
+        }
260
+
261
+        // Check that we do not share with more permissions than we have
262
+        if ($share->getPermissions() & ~$permissions) {
263
+            $message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]);
264
+            throw new GenericShareException($message_t, $message_t, 404);
265
+        }
266
+
267
+
268
+        // Check that read permissions are always set
269
+        // Link shares are allowed to have no read permissions to allow upload to hidden folders
270
+        $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
271
+            || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
272
+        if (!$noReadPermissionRequired &&
273
+            ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
274
+            throw new \InvalidArgumentException('Shares need at least read permissions');
275
+        }
276
+
277
+        if ($share->getNode() instanceof \OCP\Files\File) {
278
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
279
+                $message_t = $this->l->t('Files can’t be shared with delete permissions');
280
+                throw new GenericShareException($message_t);
281
+            }
282
+            if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
283
+                $message_t = $this->l->t('Files can’t be shared with create permissions');
284
+                throw new GenericShareException($message_t);
285
+            }
286
+        }
287
+    }
288
+
289
+    /**
290
+     * Validate if the expiration date fits the system settings
291
+     *
292
+     * @param \OCP\Share\IShare $share The share to validate the expiration date of
293
+     * @return \OCP\Share\IShare The modified share object
294
+     * @throws GenericShareException
295
+     * @throws \InvalidArgumentException
296
+     * @throws \Exception
297
+     */
298
+    protected function validateExpirationDate(\OCP\Share\IShare $share) {
299
+
300
+        $expirationDate = $share->getExpirationDate();
301
+
302
+        if ($expirationDate !== null) {
303
+            //Make sure the expiration date is a date
304
+            $expirationDate->setTime(0, 0, 0);
305
+
306
+            $date = new \DateTime();
307
+            $date->setTime(0, 0, 0);
308
+            if ($date >= $expirationDate) {
309
+                $message = $this->l->t('Expiration date is in the past');
310
+                throw new GenericShareException($message, $message, 404);
311
+            }
312
+        }
313
+
314
+        // If expiredate is empty set a default one if there is a default
315
+        $fullId = null;
316
+        try {
317
+            $fullId = $share->getFullId();
318
+        } catch (\UnexpectedValueException $e) {
319
+            // This is a new share
320
+        }
321
+
322
+        if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
323
+            $expirationDate = new \DateTime();
324
+            $expirationDate->setTime(0,0,0);
325
+            $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
326
+        }
327
+
328
+        // If we enforce the expiration date check that is does not exceed
329
+        if ($this->shareApiLinkDefaultExpireDateEnforced()) {
330
+            if ($expirationDate === null) {
331
+                throw new \InvalidArgumentException('Expiration date is enforced');
332
+            }
333
+
334
+            $date = new \DateTime();
335
+            $date->setTime(0, 0, 0);
336
+            $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
337
+            if ($date < $expirationDate) {
338
+                $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
339
+                throw new GenericShareException($message, $message, 404);
340
+            }
341
+        }
342
+
343
+        $accepted = true;
344
+        $message = '';
345
+        \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
346
+            'expirationDate' => &$expirationDate,
347
+            'accepted' => &$accepted,
348
+            'message' => &$message,
349
+            'passwordSet' => $share->getPassword() !== null,
350
+        ]);
351
+
352
+        if (!$accepted) {
353
+            throw new \Exception($message);
354
+        }
355
+
356
+        $share->setExpirationDate($expirationDate);
357
+
358
+        return $share;
359
+    }
360
+
361
+    /**
362
+     * Check for pre share requirements for user shares
363
+     *
364
+     * @param \OCP\Share\IShare $share
365
+     * @throws \Exception
366
+     */
367
+    protected function userCreateChecks(\OCP\Share\IShare $share) {
368
+        // Check if we can share with group members only
369
+        if ($this->shareWithGroupMembersOnly()) {
370
+            $sharedBy = $this->userManager->get($share->getSharedBy());
371
+            $sharedWith = $this->userManager->get($share->getSharedWith());
372
+            // Verify we can share with this user
373
+            $groups = array_intersect(
374
+                    $this->groupManager->getUserGroupIds($sharedBy),
375
+                    $this->groupManager->getUserGroupIds($sharedWith)
376
+            );
377
+            if (empty($groups)) {
378
+                throw new \Exception('Sharing is only allowed with group members');
379
+            }
380
+        }
381
+
382
+        /*
383 383
 		 * TODO: Could be costly, fix
384 384
 		 *
385 385
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
386 386
 		 */
387
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
388
-		$existingShares = $provider->getSharesByPath($share->getNode());
389
-		foreach($existingShares as $existingShare) {
390
-			// Ignore if it is the same share
391
-			try {
392
-				if ($existingShare->getFullId() === $share->getFullId()) {
393
-					continue;
394
-				}
395
-			} catch (\UnexpectedValueException $e) {
396
-				//Shares are not identical
397
-			}
398
-
399
-			// Identical share already existst
400
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
401
-				throw new \Exception('Path is already shared with this user');
402
-			}
403
-
404
-			// The share is already shared with this user via a group share
405
-			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
406
-				$group = $this->groupManager->get($existingShare->getSharedWith());
407
-				if (!is_null($group)) {
408
-					$user = $this->userManager->get($share->getSharedWith());
409
-
410
-					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
411
-						throw new \Exception('Path is already shared with this user');
412
-					}
413
-				}
414
-			}
415
-		}
416
-	}
417
-
418
-	/**
419
-	 * Check for pre share requirements for group shares
420
-	 *
421
-	 * @param \OCP\Share\IShare $share
422
-	 * @throws \Exception
423
-	 */
424
-	protected function groupCreateChecks(\OCP\Share\IShare $share) {
425
-		// Verify group shares are allowed
426
-		if (!$this->allowGroupSharing()) {
427
-			throw new \Exception('Group sharing is now allowed');
428
-		}
429
-
430
-		// Verify if the user can share with this group
431
-		if ($this->shareWithGroupMembersOnly()) {
432
-			$sharedBy = $this->userManager->get($share->getSharedBy());
433
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
434
-			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
435
-				throw new \Exception('Sharing is only allowed within your own groups');
436
-			}
437
-		}
438
-
439
-		/*
387
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
388
+        $existingShares = $provider->getSharesByPath($share->getNode());
389
+        foreach($existingShares as $existingShare) {
390
+            // Ignore if it is the same share
391
+            try {
392
+                if ($existingShare->getFullId() === $share->getFullId()) {
393
+                    continue;
394
+                }
395
+            } catch (\UnexpectedValueException $e) {
396
+                //Shares are not identical
397
+            }
398
+
399
+            // Identical share already existst
400
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
401
+                throw new \Exception('Path is already shared with this user');
402
+            }
403
+
404
+            // The share is already shared with this user via a group share
405
+            if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
406
+                $group = $this->groupManager->get($existingShare->getSharedWith());
407
+                if (!is_null($group)) {
408
+                    $user = $this->userManager->get($share->getSharedWith());
409
+
410
+                    if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
411
+                        throw new \Exception('Path is already shared with this user');
412
+                    }
413
+                }
414
+            }
415
+        }
416
+    }
417
+
418
+    /**
419
+     * Check for pre share requirements for group shares
420
+     *
421
+     * @param \OCP\Share\IShare $share
422
+     * @throws \Exception
423
+     */
424
+    protected function groupCreateChecks(\OCP\Share\IShare $share) {
425
+        // Verify group shares are allowed
426
+        if (!$this->allowGroupSharing()) {
427
+            throw new \Exception('Group sharing is now allowed');
428
+        }
429
+
430
+        // Verify if the user can share with this group
431
+        if ($this->shareWithGroupMembersOnly()) {
432
+            $sharedBy = $this->userManager->get($share->getSharedBy());
433
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
434
+            if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
435
+                throw new \Exception('Sharing is only allowed within your own groups');
436
+            }
437
+        }
438
+
439
+        /*
440 440
 		 * TODO: Could be costly, fix
441 441
 		 *
442 442
 		 * Also this is not what we want in the future.. then we want to squash identical shares.
443 443
 		 */
444
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
445
-		$existingShares = $provider->getSharesByPath($share->getNode());
446
-		foreach($existingShares as $existingShare) {
447
-			try {
448
-				if ($existingShare->getFullId() === $share->getFullId()) {
449
-					continue;
450
-				}
451
-			} catch (\UnexpectedValueException $e) {
452
-				//It is a new share so just continue
453
-			}
454
-
455
-			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
456
-				throw new \Exception('Path is already shared with this group');
457
-			}
458
-		}
459
-	}
460
-
461
-	/**
462
-	 * Check for pre share requirements for link shares
463
-	 *
464
-	 * @param \OCP\Share\IShare $share
465
-	 * @throws \Exception
466
-	 */
467
-	protected function linkCreateChecks(\OCP\Share\IShare $share) {
468
-		// Are link shares allowed?
469
-		if (!$this->shareApiAllowLinks()) {
470
-			throw new \Exception('Link sharing is not allowed');
471
-		}
472
-
473
-		// Link shares by definition can't have share permissions
474
-		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
475
-			throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
476
-		}
477
-
478
-		// Check if public upload is allowed
479
-		if (!$this->shareApiLinkAllowPublicUpload() &&
480
-			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
481
-			throw new \InvalidArgumentException('Public upload is not allowed');
482
-		}
483
-	}
484
-
485
-	/**
486
-	 * To make sure we don't get invisible link shares we set the parent
487
-	 * of a link if it is a reshare. This is a quick word around
488
-	 * until we can properly display multiple link shares in the UI
489
-	 *
490
-	 * See: https://github.com/owncloud/core/issues/22295
491
-	 *
492
-	 * FIXME: Remove once multiple link shares can be properly displayed
493
-	 *
494
-	 * @param \OCP\Share\IShare $share
495
-	 */
496
-	protected function setLinkParent(\OCP\Share\IShare $share) {
497
-
498
-		// No sense in checking if the method is not there.
499
-		if (method_exists($share, 'setParent')) {
500
-			$storage = $share->getNode()->getStorage();
501
-			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
502
-				/** @var \OCA\Files_Sharing\SharedStorage $storage */
503
-				$share->setParent($storage->getShareId());
504
-			}
505
-		};
506
-	}
507
-
508
-	/**
509
-	 * @param File|Folder $path
510
-	 */
511
-	protected function pathCreateChecks($path) {
512
-		// Make sure that we do not share a path that contains a shared mountpoint
513
-		if ($path instanceof \OCP\Files\Folder) {
514
-			$mounts = $this->mountManager->findIn($path->getPath());
515
-			foreach($mounts as $mount) {
516
-				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
517
-					throw new \InvalidArgumentException('Path contains files shared with you');
518
-				}
519
-			}
520
-		}
521
-	}
522
-
523
-	/**
524
-	 * Check if the user that is sharing can actually share
525
-	 *
526
-	 * @param \OCP\Share\IShare $share
527
-	 * @throws \Exception
528
-	 */
529
-	protected function canShare(\OCP\Share\IShare $share) {
530
-		if (!$this->shareApiEnabled()) {
531
-			throw new \Exception('Sharing is disabled');
532
-		}
533
-
534
-		if ($this->sharingDisabledForUser($share->getSharedBy())) {
535
-			throw new \Exception('Sharing is disabled for you');
536
-		}
537
-	}
538
-
539
-	/**
540
-	 * Share a path
541
-	 *
542
-	 * @param \OCP\Share\IShare $share
543
-	 * @return Share The share object
544
-	 * @throws \Exception
545
-	 *
546
-	 * TODO: handle link share permissions or check them
547
-	 */
548
-	public function createShare(\OCP\Share\IShare $share) {
549
-		$this->canShare($share);
550
-
551
-		$this->generalCreateChecks($share);
552
-
553
-		// Verify if there are any issues with the path
554
-		$this->pathCreateChecks($share->getNode());
555
-
556
-		/*
444
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
445
+        $existingShares = $provider->getSharesByPath($share->getNode());
446
+        foreach($existingShares as $existingShare) {
447
+            try {
448
+                if ($existingShare->getFullId() === $share->getFullId()) {
449
+                    continue;
450
+                }
451
+            } catch (\UnexpectedValueException $e) {
452
+                //It is a new share so just continue
453
+            }
454
+
455
+            if ($existingShare->getSharedWith() === $share->getSharedWith()) {
456
+                throw new \Exception('Path is already shared with this group');
457
+            }
458
+        }
459
+    }
460
+
461
+    /**
462
+     * Check for pre share requirements for link shares
463
+     *
464
+     * @param \OCP\Share\IShare $share
465
+     * @throws \Exception
466
+     */
467
+    protected function linkCreateChecks(\OCP\Share\IShare $share) {
468
+        // Are link shares allowed?
469
+        if (!$this->shareApiAllowLinks()) {
470
+            throw new \Exception('Link sharing is not allowed');
471
+        }
472
+
473
+        // Link shares by definition can't have share permissions
474
+        if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
475
+            throw new \InvalidArgumentException('Link shares can’t have reshare permissions');
476
+        }
477
+
478
+        // Check if public upload is allowed
479
+        if (!$this->shareApiLinkAllowPublicUpload() &&
480
+            ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
481
+            throw new \InvalidArgumentException('Public upload is not allowed');
482
+        }
483
+    }
484
+
485
+    /**
486
+     * To make sure we don't get invisible link shares we set the parent
487
+     * of a link if it is a reshare. This is a quick word around
488
+     * until we can properly display multiple link shares in the UI
489
+     *
490
+     * See: https://github.com/owncloud/core/issues/22295
491
+     *
492
+     * FIXME: Remove once multiple link shares can be properly displayed
493
+     *
494
+     * @param \OCP\Share\IShare $share
495
+     */
496
+    protected function setLinkParent(\OCP\Share\IShare $share) {
497
+
498
+        // No sense in checking if the method is not there.
499
+        if (method_exists($share, 'setParent')) {
500
+            $storage = $share->getNode()->getStorage();
501
+            if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
502
+                /** @var \OCA\Files_Sharing\SharedStorage $storage */
503
+                $share->setParent($storage->getShareId());
504
+            }
505
+        };
506
+    }
507
+
508
+    /**
509
+     * @param File|Folder $path
510
+     */
511
+    protected function pathCreateChecks($path) {
512
+        // Make sure that we do not share a path that contains a shared mountpoint
513
+        if ($path instanceof \OCP\Files\Folder) {
514
+            $mounts = $this->mountManager->findIn($path->getPath());
515
+            foreach($mounts as $mount) {
516
+                if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
517
+                    throw new \InvalidArgumentException('Path contains files shared with you');
518
+                }
519
+            }
520
+        }
521
+    }
522
+
523
+    /**
524
+     * Check if the user that is sharing can actually share
525
+     *
526
+     * @param \OCP\Share\IShare $share
527
+     * @throws \Exception
528
+     */
529
+    protected function canShare(\OCP\Share\IShare $share) {
530
+        if (!$this->shareApiEnabled()) {
531
+            throw new \Exception('Sharing is disabled');
532
+        }
533
+
534
+        if ($this->sharingDisabledForUser($share->getSharedBy())) {
535
+            throw new \Exception('Sharing is disabled for you');
536
+        }
537
+    }
538
+
539
+    /**
540
+     * Share a path
541
+     *
542
+     * @param \OCP\Share\IShare $share
543
+     * @return Share The share object
544
+     * @throws \Exception
545
+     *
546
+     * TODO: handle link share permissions or check them
547
+     */
548
+    public function createShare(\OCP\Share\IShare $share) {
549
+        $this->canShare($share);
550
+
551
+        $this->generalCreateChecks($share);
552
+
553
+        // Verify if there are any issues with the path
554
+        $this->pathCreateChecks($share->getNode());
555
+
556
+        /*
557 557
 		 * On creation of a share the owner is always the owner of the path
558 558
 		 * Except for mounted federated shares.
559 559
 		 */
560
-		$storage = $share->getNode()->getStorage();
561
-		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
562
-			$parent = $share->getNode()->getParent();
563
-			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
564
-				$parent = $parent->getParent();
565
-			}
566
-			$share->setShareOwner($parent->getOwner()->getUID());
567
-		} else {
568
-			$share->setShareOwner($share->getNode()->getOwner()->getUID());
569
-		}
570
-
571
-		//Verify share type
572
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
573
-			$this->userCreateChecks($share);
574
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
575
-			$this->groupCreateChecks($share);
576
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
577
-			$this->linkCreateChecks($share);
578
-			$this->setLinkParent($share);
579
-
580
-			/*
560
+        $storage = $share->getNode()->getStorage();
561
+        if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
562
+            $parent = $share->getNode()->getParent();
563
+            while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
564
+                $parent = $parent->getParent();
565
+            }
566
+            $share->setShareOwner($parent->getOwner()->getUID());
567
+        } else {
568
+            $share->setShareOwner($share->getNode()->getOwner()->getUID());
569
+        }
570
+
571
+        //Verify share type
572
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
573
+            $this->userCreateChecks($share);
574
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
575
+            $this->groupCreateChecks($share);
576
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
577
+            $this->linkCreateChecks($share);
578
+            $this->setLinkParent($share);
579
+
580
+            /*
581 581
 			 * For now ignore a set token.
582 582
 			 */
583
-			$share->setToken(
584
-				$this->secureRandom->generate(
585
-					\OC\Share\Constants::TOKEN_LENGTH,
586
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
587
-				)
588
-			);
589
-
590
-			//Verify the expiration date
591
-			$this->validateExpirationDate($share);
592
-
593
-			//Verify the password
594
-			$this->verifyPassword($share->getPassword());
595
-
596
-			// If a password is set. Hash it!
597
-			if ($share->getPassword() !== null) {
598
-				$share->setPassword($this->hasher->hash($share->getPassword()));
599
-			}
600
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
601
-			$share->setToken(
602
-				$this->secureRandom->generate(
603
-					\OC\Share\Constants::TOKEN_LENGTH,
604
-					\OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
605
-				)
606
-			);
607
-		}
608
-
609
-		// Cannot share with the owner
610
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
611
-			$share->getSharedWith() === $share->getShareOwner()) {
612
-			throw new \InvalidArgumentException('Can’t share with the share owner');
613
-		}
614
-
615
-		// Generate the target
616
-		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
617
-		$target = \OC\Files\Filesystem::normalizePath($target);
618
-		$share->setTarget($target);
619
-
620
-		// Pre share hook
621
-		$run = true;
622
-		$error = '';
623
-		$preHookData = [
624
-			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
625
-			'itemSource' => $share->getNode()->getId(),
626
-			'shareType' => $share->getShareType(),
627
-			'uidOwner' => $share->getSharedBy(),
628
-			'permissions' => $share->getPermissions(),
629
-			'fileSource' => $share->getNode()->getId(),
630
-			'expiration' => $share->getExpirationDate(),
631
-			'token' => $share->getToken(),
632
-			'itemTarget' => $share->getTarget(),
633
-			'shareWith' => $share->getSharedWith(),
634
-			'run' => &$run,
635
-			'error' => &$error,
636
-		];
637
-		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
638
-
639
-		if ($run === false) {
640
-			throw new \Exception($error);
641
-		}
642
-
643
-		$oldShare = $share;
644
-		$provider = $this->factory->getProviderForType($share->getShareType());
645
-		$share = $provider->create($share);
646
-		//reuse the node we already have
647
-		$share->setNode($oldShare->getNode());
648
-
649
-		// Post share hook
650
-		$postHookData = [
651
-			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
652
-			'itemSource' => $share->getNode()->getId(),
653
-			'shareType' => $share->getShareType(),
654
-			'uidOwner' => $share->getSharedBy(),
655
-			'permissions' => $share->getPermissions(),
656
-			'fileSource' => $share->getNode()->getId(),
657
-			'expiration' => $share->getExpirationDate(),
658
-			'token' => $share->getToken(),
659
-			'id' => $share->getId(),
660
-			'shareWith' => $share->getSharedWith(),
661
-			'itemTarget' => $share->getTarget(),
662
-			'fileTarget' => $share->getTarget(),
663
-		];
664
-
665
-		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
666
-
667
-		return $share;
668
-	}
669
-
670
-	/**
671
-	 * Update a share
672
-	 *
673
-	 * @param \OCP\Share\IShare $share
674
-	 * @return \OCP\Share\IShare The share object
675
-	 * @throws \InvalidArgumentException
676
-	 */
677
-	public function updateShare(\OCP\Share\IShare $share) {
678
-		$expirationDateUpdated = false;
679
-
680
-		$this->canShare($share);
681
-
682
-		try {
683
-			$originalShare = $this->getShareById($share->getFullId());
684
-		} catch (\UnexpectedValueException $e) {
685
-			throw new \InvalidArgumentException('Share does not have a full id');
686
-		}
687
-
688
-		// We can't change the share type!
689
-		if ($share->getShareType() !== $originalShare->getShareType()) {
690
-			throw new \InvalidArgumentException('Can’t change share type');
691
-		}
692
-
693
-		// We can only change the recipient on user shares
694
-		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
695
-		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
696
-			throw new \InvalidArgumentException('Can only update recipient on user shares');
697
-		}
698
-
699
-		// Cannot share with the owner
700
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
701
-			$share->getSharedWith() === $share->getShareOwner()) {
702
-			throw new \InvalidArgumentException('Can’t share with the share owner');
703
-		}
704
-
705
-		$this->generalCreateChecks($share);
706
-
707
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
708
-			$this->userCreateChecks($share);
709
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
710
-			$this->groupCreateChecks($share);
711
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
712
-			$this->linkCreateChecks($share);
713
-
714
-			$this->updateSharePasswordIfNeeded($share, $originalShare);
715
-
716
-			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
717
-				//Verify the expiration date
718
-				$this->validateExpirationDate($share);
719
-				$expirationDateUpdated = true;
720
-			}
721
-		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
722
-			$plainTextPassword = $share->getPassword();
723
-			if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
724
-				$plainTextPassword = null;
725
-			}
726
-		}
727
-
728
-		$this->pathCreateChecks($share->getNode());
729
-
730
-		// Now update the share!
731
-		$provider = $this->factory->getProviderForType($share->getShareType());
732
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
733
-			$share = $provider->update($share, $plainTextPassword);
734
-		} else {
735
-			$share = $provider->update($share);
736
-		}
737
-
738
-		if ($expirationDateUpdated === true) {
739
-			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
740
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
741
-				'itemSource' => $share->getNode()->getId(),
742
-				'date' => $share->getExpirationDate(),
743
-				'uidOwner' => $share->getSharedBy(),
744
-			]);
745
-		}
746
-
747
-		if ($share->getPassword() !== $originalShare->getPassword()) {
748
-			\OC_Hook::emit('OCP\Share', 'post_update_password', [
749
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
750
-				'itemSource' => $share->getNode()->getId(),
751
-				'uidOwner' => $share->getSharedBy(),
752
-				'token' => $share->getToken(),
753
-				'disabled' => is_null($share->getPassword()),
754
-			]);
755
-		}
756
-
757
-		if ($share->getPermissions() !== $originalShare->getPermissions()) {
758
-			if ($this->userManager->userExists($share->getShareOwner())) {
759
-				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
760
-			} else {
761
-				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
762
-			}
763
-			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
764
-				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
765
-				'itemSource' => $share->getNode()->getId(),
766
-				'shareType' => $share->getShareType(),
767
-				'shareWith' => $share->getSharedWith(),
768
-				'uidOwner' => $share->getSharedBy(),
769
-				'permissions' => $share->getPermissions(),
770
-				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
771
-			));
772
-		}
773
-
774
-		return $share;
775
-	}
776
-
777
-	/**
778
-	 * Updates the password of the given share if it is not the same as the
779
-	 * password of the original share.
780
-	 *
781
-	 * @param \OCP\Share\IShare $share the share to update its password.
782
-	 * @param \OCP\Share\IShare $originalShare the original share to compare its
783
-	 *        password with.
784
-	 * @return boolean whether the password was updated or not.
785
-	 */
786
-	private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
787
-		// Password updated.
788
-		if ($share->getPassword() !== $originalShare->getPassword()) {
789
-			//Verify the password
790
-			$this->verifyPassword($share->getPassword());
791
-
792
-			// If a password is set. Hash it!
793
-			if ($share->getPassword() !== null) {
794
-				$share->setPassword($this->hasher->hash($share->getPassword()));
795
-
796
-				return true;
797
-			}
798
-		}
799
-
800
-		return false;
801
-	}
802
-
803
-	/**
804
-	 * Delete all the children of this share
805
-	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
806
-	 *
807
-	 * @param \OCP\Share\IShare $share
808
-	 * @return \OCP\Share\IShare[] List of deleted shares
809
-	 */
810
-	protected function deleteChildren(\OCP\Share\IShare $share) {
811
-		$deletedShares = [];
812
-
813
-		$provider = $this->factory->getProviderForType($share->getShareType());
814
-
815
-		foreach ($provider->getChildren($share) as $child) {
816
-			$deletedChildren = $this->deleteChildren($child);
817
-			$deletedShares = array_merge($deletedShares, $deletedChildren);
818
-
819
-			$provider->delete($child);
820
-			$deletedShares[] = $child;
821
-		}
822
-
823
-		return $deletedShares;
824
-	}
825
-
826
-	/**
827
-	 * Delete a share
828
-	 *
829
-	 * @param \OCP\Share\IShare $share
830
-	 * @throws ShareNotFound
831
-	 * @throws \InvalidArgumentException
832
-	 */
833
-	public function deleteShare(\OCP\Share\IShare $share) {
834
-
835
-		try {
836
-			$share->getFullId();
837
-		} catch (\UnexpectedValueException $e) {
838
-			throw new \InvalidArgumentException('Share does not have a full id');
839
-		}
840
-
841
-		$event = new GenericEvent($share);
842
-		$this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
843
-
844
-		// Get all children and delete them as well
845
-		$deletedShares = $this->deleteChildren($share);
846
-
847
-		// Do the actual delete
848
-		$provider = $this->factory->getProviderForType($share->getShareType());
849
-		$provider->delete($share);
850
-
851
-		// All the deleted shares caused by this delete
852
-		$deletedShares[] = $share;
853
-
854
-		// Emit post hook
855
-		$event->setArgument('deletedShares', $deletedShares);
856
-		$this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
857
-	}
858
-
859
-
860
-	/**
861
-	 * Unshare a file as the recipient.
862
-	 * This can be different from a regular delete for example when one of
863
-	 * the users in a groups deletes that share. But the provider should
864
-	 * handle this.
865
-	 *
866
-	 * @param \OCP\Share\IShare $share
867
-	 * @param string $recipientId
868
-	 */
869
-	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
870
-		list($providerId, ) = $this->splitFullId($share->getFullId());
871
-		$provider = $this->factory->getProvider($providerId);
872
-
873
-		$provider->deleteFromSelf($share, $recipientId);
874
-	}
875
-
876
-	/**
877
-	 * @inheritdoc
878
-	 */
879
-	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
880
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
881
-			throw new \InvalidArgumentException('Can’t change target of link share');
882
-		}
883
-
884
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
885
-			throw new \InvalidArgumentException('Invalid recipient');
886
-		}
887
-
888
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
889
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
890
-			if (is_null($sharedWith)) {
891
-				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
892
-			}
893
-			$recipient = $this->userManager->get($recipientId);
894
-			if (!$sharedWith->inGroup($recipient)) {
895
-				throw new \InvalidArgumentException('Invalid recipient');
896
-			}
897
-		}
898
-
899
-		list($providerId, ) = $this->splitFullId($share->getFullId());
900
-		$provider = $this->factory->getProvider($providerId);
901
-
902
-		$provider->move($share, $recipientId);
903
-	}
904
-
905
-	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
906
-		$providers = $this->factory->getAllProviders();
907
-
908
-		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
909
-			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
910
-			foreach ($newShares as $fid => $data) {
911
-				if (!isset($shares[$fid])) {
912
-					$shares[$fid] = [];
913
-				}
914
-
915
-				$shares[$fid] = array_merge($shares[$fid], $data);
916
-			}
917
-			return $shares;
918
-		}, []);
919
-	}
920
-
921
-	/**
922
-	 * @inheritdoc
923
-	 */
924
-	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
925
-		if ($path !== null &&
926
-				!($path instanceof \OCP\Files\File) &&
927
-				!($path instanceof \OCP\Files\Folder)) {
928
-			throw new \InvalidArgumentException('invalid path');
929
-		}
930
-
931
-		try {
932
-			$provider = $this->factory->getProviderForType($shareType);
933
-		} catch (ProviderException $e) {
934
-			return [];
935
-		}
936
-
937
-		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
938
-
939
-		/*
583
+            $share->setToken(
584
+                $this->secureRandom->generate(
585
+                    \OC\Share\Constants::TOKEN_LENGTH,
586
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
587
+                )
588
+            );
589
+
590
+            //Verify the expiration date
591
+            $this->validateExpirationDate($share);
592
+
593
+            //Verify the password
594
+            $this->verifyPassword($share->getPassword());
595
+
596
+            // If a password is set. Hash it!
597
+            if ($share->getPassword() !== null) {
598
+                $share->setPassword($this->hasher->hash($share->getPassword()));
599
+            }
600
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
601
+            $share->setToken(
602
+                $this->secureRandom->generate(
603
+                    \OC\Share\Constants::TOKEN_LENGTH,
604
+                    \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
605
+                )
606
+            );
607
+        }
608
+
609
+        // Cannot share with the owner
610
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
611
+            $share->getSharedWith() === $share->getShareOwner()) {
612
+            throw new \InvalidArgumentException('Can’t share with the share owner');
613
+        }
614
+
615
+        // Generate the target
616
+        $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
617
+        $target = \OC\Files\Filesystem::normalizePath($target);
618
+        $share->setTarget($target);
619
+
620
+        // Pre share hook
621
+        $run = true;
622
+        $error = '';
623
+        $preHookData = [
624
+            'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
625
+            'itemSource' => $share->getNode()->getId(),
626
+            'shareType' => $share->getShareType(),
627
+            'uidOwner' => $share->getSharedBy(),
628
+            'permissions' => $share->getPermissions(),
629
+            'fileSource' => $share->getNode()->getId(),
630
+            'expiration' => $share->getExpirationDate(),
631
+            'token' => $share->getToken(),
632
+            'itemTarget' => $share->getTarget(),
633
+            'shareWith' => $share->getSharedWith(),
634
+            'run' => &$run,
635
+            'error' => &$error,
636
+        ];
637
+        \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
638
+
639
+        if ($run === false) {
640
+            throw new \Exception($error);
641
+        }
642
+
643
+        $oldShare = $share;
644
+        $provider = $this->factory->getProviderForType($share->getShareType());
645
+        $share = $provider->create($share);
646
+        //reuse the node we already have
647
+        $share->setNode($oldShare->getNode());
648
+
649
+        // Post share hook
650
+        $postHookData = [
651
+            'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
652
+            'itemSource' => $share->getNode()->getId(),
653
+            'shareType' => $share->getShareType(),
654
+            'uidOwner' => $share->getSharedBy(),
655
+            'permissions' => $share->getPermissions(),
656
+            'fileSource' => $share->getNode()->getId(),
657
+            'expiration' => $share->getExpirationDate(),
658
+            'token' => $share->getToken(),
659
+            'id' => $share->getId(),
660
+            'shareWith' => $share->getSharedWith(),
661
+            'itemTarget' => $share->getTarget(),
662
+            'fileTarget' => $share->getTarget(),
663
+        ];
664
+
665
+        \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
666
+
667
+        return $share;
668
+    }
669
+
670
+    /**
671
+     * Update a share
672
+     *
673
+     * @param \OCP\Share\IShare $share
674
+     * @return \OCP\Share\IShare The share object
675
+     * @throws \InvalidArgumentException
676
+     */
677
+    public function updateShare(\OCP\Share\IShare $share) {
678
+        $expirationDateUpdated = false;
679
+
680
+        $this->canShare($share);
681
+
682
+        try {
683
+            $originalShare = $this->getShareById($share->getFullId());
684
+        } catch (\UnexpectedValueException $e) {
685
+            throw new \InvalidArgumentException('Share does not have a full id');
686
+        }
687
+
688
+        // We can't change the share type!
689
+        if ($share->getShareType() !== $originalShare->getShareType()) {
690
+            throw new \InvalidArgumentException('Can’t change share type');
691
+        }
692
+
693
+        // We can only change the recipient on user shares
694
+        if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
695
+            $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
696
+            throw new \InvalidArgumentException('Can only update recipient on user shares');
697
+        }
698
+
699
+        // Cannot share with the owner
700
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
701
+            $share->getSharedWith() === $share->getShareOwner()) {
702
+            throw new \InvalidArgumentException('Can’t share with the share owner');
703
+        }
704
+
705
+        $this->generalCreateChecks($share);
706
+
707
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
708
+            $this->userCreateChecks($share);
709
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
710
+            $this->groupCreateChecks($share);
711
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
712
+            $this->linkCreateChecks($share);
713
+
714
+            $this->updateSharePasswordIfNeeded($share, $originalShare);
715
+
716
+            if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
717
+                //Verify the expiration date
718
+                $this->validateExpirationDate($share);
719
+                $expirationDateUpdated = true;
720
+            }
721
+        } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
722
+            $plainTextPassword = $share->getPassword();
723
+            if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) {
724
+                $plainTextPassword = null;
725
+            }
726
+        }
727
+
728
+        $this->pathCreateChecks($share->getNode());
729
+
730
+        // Now update the share!
731
+        $provider = $this->factory->getProviderForType($share->getShareType());
732
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
733
+            $share = $provider->update($share, $plainTextPassword);
734
+        } else {
735
+            $share = $provider->update($share);
736
+        }
737
+
738
+        if ($expirationDateUpdated === true) {
739
+            \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
740
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
741
+                'itemSource' => $share->getNode()->getId(),
742
+                'date' => $share->getExpirationDate(),
743
+                'uidOwner' => $share->getSharedBy(),
744
+            ]);
745
+        }
746
+
747
+        if ($share->getPassword() !== $originalShare->getPassword()) {
748
+            \OC_Hook::emit('OCP\Share', 'post_update_password', [
749
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
750
+                'itemSource' => $share->getNode()->getId(),
751
+                'uidOwner' => $share->getSharedBy(),
752
+                'token' => $share->getToken(),
753
+                'disabled' => is_null($share->getPassword()),
754
+            ]);
755
+        }
756
+
757
+        if ($share->getPermissions() !== $originalShare->getPermissions()) {
758
+            if ($this->userManager->userExists($share->getShareOwner())) {
759
+                $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
760
+            } else {
761
+                $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
762
+            }
763
+            \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
764
+                'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
765
+                'itemSource' => $share->getNode()->getId(),
766
+                'shareType' => $share->getShareType(),
767
+                'shareWith' => $share->getSharedWith(),
768
+                'uidOwner' => $share->getSharedBy(),
769
+                'permissions' => $share->getPermissions(),
770
+                'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
771
+            ));
772
+        }
773
+
774
+        return $share;
775
+    }
776
+
777
+    /**
778
+     * Updates the password of the given share if it is not the same as the
779
+     * password of the original share.
780
+     *
781
+     * @param \OCP\Share\IShare $share the share to update its password.
782
+     * @param \OCP\Share\IShare $originalShare the original share to compare its
783
+     *        password with.
784
+     * @return boolean whether the password was updated or not.
785
+     */
786
+    private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) {
787
+        // Password updated.
788
+        if ($share->getPassword() !== $originalShare->getPassword()) {
789
+            //Verify the password
790
+            $this->verifyPassword($share->getPassword());
791
+
792
+            // If a password is set. Hash it!
793
+            if ($share->getPassword() !== null) {
794
+                $share->setPassword($this->hasher->hash($share->getPassword()));
795
+
796
+                return true;
797
+            }
798
+        }
799
+
800
+        return false;
801
+    }
802
+
803
+    /**
804
+     * Delete all the children of this share
805
+     * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
806
+     *
807
+     * @param \OCP\Share\IShare $share
808
+     * @return \OCP\Share\IShare[] List of deleted shares
809
+     */
810
+    protected function deleteChildren(\OCP\Share\IShare $share) {
811
+        $deletedShares = [];
812
+
813
+        $provider = $this->factory->getProviderForType($share->getShareType());
814
+
815
+        foreach ($provider->getChildren($share) as $child) {
816
+            $deletedChildren = $this->deleteChildren($child);
817
+            $deletedShares = array_merge($deletedShares, $deletedChildren);
818
+
819
+            $provider->delete($child);
820
+            $deletedShares[] = $child;
821
+        }
822
+
823
+        return $deletedShares;
824
+    }
825
+
826
+    /**
827
+     * Delete a share
828
+     *
829
+     * @param \OCP\Share\IShare $share
830
+     * @throws ShareNotFound
831
+     * @throws \InvalidArgumentException
832
+     */
833
+    public function deleteShare(\OCP\Share\IShare $share) {
834
+
835
+        try {
836
+            $share->getFullId();
837
+        } catch (\UnexpectedValueException $e) {
838
+            throw new \InvalidArgumentException('Share does not have a full id');
839
+        }
840
+
841
+        $event = new GenericEvent($share);
842
+        $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
843
+
844
+        // Get all children and delete them as well
845
+        $deletedShares = $this->deleteChildren($share);
846
+
847
+        // Do the actual delete
848
+        $provider = $this->factory->getProviderForType($share->getShareType());
849
+        $provider->delete($share);
850
+
851
+        // All the deleted shares caused by this delete
852
+        $deletedShares[] = $share;
853
+
854
+        // Emit post hook
855
+        $event->setArgument('deletedShares', $deletedShares);
856
+        $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
857
+    }
858
+
859
+
860
+    /**
861
+     * Unshare a file as the recipient.
862
+     * This can be different from a regular delete for example when one of
863
+     * the users in a groups deletes that share. But the provider should
864
+     * handle this.
865
+     *
866
+     * @param \OCP\Share\IShare $share
867
+     * @param string $recipientId
868
+     */
869
+    public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
870
+        list($providerId, ) = $this->splitFullId($share->getFullId());
871
+        $provider = $this->factory->getProvider($providerId);
872
+
873
+        $provider->deleteFromSelf($share, $recipientId);
874
+    }
875
+
876
+    /**
877
+     * @inheritdoc
878
+     */
879
+    public function moveShare(\OCP\Share\IShare $share, $recipientId) {
880
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
881
+            throw new \InvalidArgumentException('Can’t change target of link share');
882
+        }
883
+
884
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
885
+            throw new \InvalidArgumentException('Invalid recipient');
886
+        }
887
+
888
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
889
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
890
+            if (is_null($sharedWith)) {
891
+                throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
892
+            }
893
+            $recipient = $this->userManager->get($recipientId);
894
+            if (!$sharedWith->inGroup($recipient)) {
895
+                throw new \InvalidArgumentException('Invalid recipient');
896
+            }
897
+        }
898
+
899
+        list($providerId, ) = $this->splitFullId($share->getFullId());
900
+        $provider = $this->factory->getProvider($providerId);
901
+
902
+        $provider->move($share, $recipientId);
903
+    }
904
+
905
+    public function getSharesInFolder($userId, Folder $node, $reshares = false) {
906
+        $providers = $this->factory->getAllProviders();
907
+
908
+        return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
909
+            $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
910
+            foreach ($newShares as $fid => $data) {
911
+                if (!isset($shares[$fid])) {
912
+                    $shares[$fid] = [];
913
+                }
914
+
915
+                $shares[$fid] = array_merge($shares[$fid], $data);
916
+            }
917
+            return $shares;
918
+        }, []);
919
+    }
920
+
921
+    /**
922
+     * @inheritdoc
923
+     */
924
+    public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
925
+        if ($path !== null &&
926
+                !($path instanceof \OCP\Files\File) &&
927
+                !($path instanceof \OCP\Files\Folder)) {
928
+            throw new \InvalidArgumentException('invalid path');
929
+        }
930
+
931
+        try {
932
+            $provider = $this->factory->getProviderForType($shareType);
933
+        } catch (ProviderException $e) {
934
+            return [];
935
+        }
936
+
937
+        $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
938
+
939
+        /*
940 940
 		 * Work around so we don't return expired shares but still follow
941 941
 		 * proper pagination.
942 942
 		 */
943 943
 
944
-		$shares2 = [];
945
-
946
-		while(true) {
947
-			$added = 0;
948
-			foreach ($shares as $share) {
949
-
950
-				try {
951
-					$this->checkExpireDate($share);
952
-				} catch (ShareNotFound $e) {
953
-					//Ignore since this basically means the share is deleted
954
-					continue;
955
-				}
956
-
957
-				$added++;
958
-				$shares2[] = $share;
959
-
960
-				if (count($shares2) === $limit) {
961
-					break;
962
-				}
963
-			}
964
-
965
-			if (count($shares2) === $limit) {
966
-				break;
967
-			}
968
-
969
-			// If there was no limit on the select we are done
970
-			if ($limit === -1) {
971
-				break;
972
-			}
973
-
974
-			$offset += $added;
975
-
976
-			// Fetch again $limit shares
977
-			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
978
-
979
-			// No more shares means we are done
980
-			if (empty($shares)) {
981
-				break;
982
-			}
983
-		}
984
-
985
-		$shares = $shares2;
986
-
987
-		return $shares;
988
-	}
989
-
990
-	/**
991
-	 * @inheritdoc
992
-	 */
993
-	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
994
-		try {
995
-			$provider = $this->factory->getProviderForType($shareType);
996
-		} catch (ProviderException $e) {
997
-			return [];
998
-		}
999
-
1000
-		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1001
-
1002
-		// remove all shares which are already expired
1003
-		foreach ($shares as $key => $share) {
1004
-			try {
1005
-				$this->checkExpireDate($share);
1006
-			} catch (ShareNotFound $e) {
1007
-				unset($shares[$key]);
1008
-			}
1009
-		}
1010
-
1011
-		return $shares;
1012
-	}
1013
-
1014
-	/**
1015
-	 * @inheritdoc
1016
-	 */
1017
-	public function getShareById($id, $recipient = null) {
1018
-		if ($id === null) {
1019
-			throw new ShareNotFound();
1020
-		}
1021
-
1022
-		list($providerId, $id) = $this->splitFullId($id);
1023
-
1024
-		try {
1025
-			$provider = $this->factory->getProvider($providerId);
1026
-		} catch (ProviderException $e) {
1027
-			throw new ShareNotFound();
1028
-		}
1029
-
1030
-		$share = $provider->getShareById($id, $recipient);
1031
-
1032
-		$this->checkExpireDate($share);
1033
-
1034
-		return $share;
1035
-	}
1036
-
1037
-	/**
1038
-	 * Get all the shares for a given path
1039
-	 *
1040
-	 * @param \OCP\Files\Node $path
1041
-	 * @param int $page
1042
-	 * @param int $perPage
1043
-	 *
1044
-	 * @return Share[]
1045
-	 */
1046
-	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1047
-		return [];
1048
-	}
1049
-
1050
-	/**
1051
-	 * Get the share by token possible with password
1052
-	 *
1053
-	 * @param string $token
1054
-	 * @return Share
1055
-	 *
1056
-	 * @throws ShareNotFound
1057
-	 */
1058
-	public function getShareByToken($token) {
1059
-		$share = null;
1060
-		try {
1061
-			if($this->shareApiAllowLinks()) {
1062
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1063
-				$share = $provider->getShareByToken($token);
1064
-			}
1065
-		} catch (ProviderException $e) {
1066
-		} catch (ShareNotFound $e) {
1067
-		}
1068
-
1069
-
1070
-		// If it is not a link share try to fetch a federated share by token
1071
-		if ($share === null) {
1072
-			try {
1073
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1074
-				$share = $provider->getShareByToken($token);
1075
-			} catch (ProviderException $e) {
1076
-			} catch (ShareNotFound $e) {
1077
-			}
1078
-		}
1079
-
1080
-		// If it is not a link share try to fetch a mail share by token
1081
-		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1082
-			try {
1083
-				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1084
-				$share = $provider->getShareByToken($token);
1085
-			} catch (ProviderException $e) {
1086
-			} catch (ShareNotFound $e) {
1087
-			}
1088
-		}
1089
-
1090
-		if ($share === null) {
1091
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1092
-		}
1093
-
1094
-		$this->checkExpireDate($share);
1095
-
1096
-		/*
944
+        $shares2 = [];
945
+
946
+        while(true) {
947
+            $added = 0;
948
+            foreach ($shares as $share) {
949
+
950
+                try {
951
+                    $this->checkExpireDate($share);
952
+                } catch (ShareNotFound $e) {
953
+                    //Ignore since this basically means the share is deleted
954
+                    continue;
955
+                }
956
+
957
+                $added++;
958
+                $shares2[] = $share;
959
+
960
+                if (count($shares2) === $limit) {
961
+                    break;
962
+                }
963
+            }
964
+
965
+            if (count($shares2) === $limit) {
966
+                break;
967
+            }
968
+
969
+            // If there was no limit on the select we are done
970
+            if ($limit === -1) {
971
+                break;
972
+            }
973
+
974
+            $offset += $added;
975
+
976
+            // Fetch again $limit shares
977
+            $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
978
+
979
+            // No more shares means we are done
980
+            if (empty($shares)) {
981
+                break;
982
+            }
983
+        }
984
+
985
+        $shares = $shares2;
986
+
987
+        return $shares;
988
+    }
989
+
990
+    /**
991
+     * @inheritdoc
992
+     */
993
+    public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
994
+        try {
995
+            $provider = $this->factory->getProviderForType($shareType);
996
+        } catch (ProviderException $e) {
997
+            return [];
998
+        }
999
+
1000
+        $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
1001
+
1002
+        // remove all shares which are already expired
1003
+        foreach ($shares as $key => $share) {
1004
+            try {
1005
+                $this->checkExpireDate($share);
1006
+            } catch (ShareNotFound $e) {
1007
+                unset($shares[$key]);
1008
+            }
1009
+        }
1010
+
1011
+        return $shares;
1012
+    }
1013
+
1014
+    /**
1015
+     * @inheritdoc
1016
+     */
1017
+    public function getShareById($id, $recipient = null) {
1018
+        if ($id === null) {
1019
+            throw new ShareNotFound();
1020
+        }
1021
+
1022
+        list($providerId, $id) = $this->splitFullId($id);
1023
+
1024
+        try {
1025
+            $provider = $this->factory->getProvider($providerId);
1026
+        } catch (ProviderException $e) {
1027
+            throw new ShareNotFound();
1028
+        }
1029
+
1030
+        $share = $provider->getShareById($id, $recipient);
1031
+
1032
+        $this->checkExpireDate($share);
1033
+
1034
+        return $share;
1035
+    }
1036
+
1037
+    /**
1038
+     * Get all the shares for a given path
1039
+     *
1040
+     * @param \OCP\Files\Node $path
1041
+     * @param int $page
1042
+     * @param int $perPage
1043
+     *
1044
+     * @return Share[]
1045
+     */
1046
+    public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
1047
+        return [];
1048
+    }
1049
+
1050
+    /**
1051
+     * Get the share by token possible with password
1052
+     *
1053
+     * @param string $token
1054
+     * @return Share
1055
+     *
1056
+     * @throws ShareNotFound
1057
+     */
1058
+    public function getShareByToken($token) {
1059
+        $share = null;
1060
+        try {
1061
+            if($this->shareApiAllowLinks()) {
1062
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1063
+                $share = $provider->getShareByToken($token);
1064
+            }
1065
+        } catch (ProviderException $e) {
1066
+        } catch (ShareNotFound $e) {
1067
+        }
1068
+
1069
+
1070
+        // If it is not a link share try to fetch a federated share by token
1071
+        if ($share === null) {
1072
+            try {
1073
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1074
+                $share = $provider->getShareByToken($token);
1075
+            } catch (ProviderException $e) {
1076
+            } catch (ShareNotFound $e) {
1077
+            }
1078
+        }
1079
+
1080
+        // If it is not a link share try to fetch a mail share by token
1081
+        if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1082
+            try {
1083
+                $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1084
+                $share = $provider->getShareByToken($token);
1085
+            } catch (ProviderException $e) {
1086
+            } catch (ShareNotFound $e) {
1087
+            }
1088
+        }
1089
+
1090
+        if ($share === null) {
1091
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1092
+        }
1093
+
1094
+        $this->checkExpireDate($share);
1095
+
1096
+        /*
1097 1097
 		 * Reduce the permissions for link shares if public upload is not enabled
1098 1098
 		 */
1099
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1100
-			!$this->shareApiLinkAllowPublicUpload()) {
1101
-			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1102
-		}
1103
-
1104
-		return $share;
1105
-	}
1106
-
1107
-	protected function checkExpireDate($share) {
1108
-		if ($share->getExpirationDate() !== null &&
1109
-			$share->getExpirationDate() <= new \DateTime()) {
1110
-			$this->deleteShare($share);
1111
-			throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1112
-		}
1113
-
1114
-	}
1115
-
1116
-	/**
1117
-	 * Verify the password of a public share
1118
-	 *
1119
-	 * @param \OCP\Share\IShare $share
1120
-	 * @param string $password
1121
-	 * @return bool
1122
-	 */
1123
-	public function checkPassword(\OCP\Share\IShare $share, $password) {
1124
-		$passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1125
-			|| $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1126
-		if (!$passwordProtected) {
1127
-			//TODO maybe exception?
1128
-			return false;
1129
-		}
1130
-
1131
-		if ($password === null || $share->getPassword() === null) {
1132
-			return false;
1133
-		}
1134
-
1135
-		$newHash = '';
1136
-		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1137
-			return false;
1138
-		}
1139
-
1140
-		if (!empty($newHash)) {
1141
-			$share->setPassword($newHash);
1142
-			$provider = $this->factory->getProviderForType($share->getShareType());
1143
-			$provider->update($share);
1144
-		}
1145
-
1146
-		return true;
1147
-	}
1148
-
1149
-	/**
1150
-	 * @inheritdoc
1151
-	 */
1152
-	public function userDeleted($uid) {
1153
-		$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];
1154
-
1155
-		foreach ($types as $type) {
1156
-			try {
1157
-				$provider = $this->factory->getProviderForType($type);
1158
-			} catch (ProviderException $e) {
1159
-				continue;
1160
-			}
1161
-			$provider->userDeleted($uid, $type);
1162
-		}
1163
-	}
1164
-
1165
-	/**
1166
-	 * @inheritdoc
1167
-	 */
1168
-	public function groupDeleted($gid) {
1169
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1170
-		$provider->groupDeleted($gid);
1171
-	}
1172
-
1173
-	/**
1174
-	 * @inheritdoc
1175
-	 */
1176
-	public function userDeletedFromGroup($uid, $gid) {
1177
-		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1178
-		$provider->userDeletedFromGroup($uid, $gid);
1179
-	}
1180
-
1181
-	/**
1182
-	 * Get access list to a path. This means
1183
-	 * all the users that can access a given path.
1184
-	 *
1185
-	 * Consider:
1186
-	 * -root
1187
-	 * |-folder1 (23)
1188
-	 *  |-folder2 (32)
1189
-	 *   |-fileA (42)
1190
-	 *
1191
-	 * fileA is shared with user1 and user1@server1
1192
-	 * folder2 is shared with group2 (user4 is a member of group2)
1193
-	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1194
-	 *
1195
-	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1196
-	 * [
1197
-	 *  users  => [
1198
-	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1199
-	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1200
-	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1201
-	 *  ],
1202
-	 *  remote => [
1203
-	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1204
-	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1205
-	 *  ],
1206
-	 *  public => bool
1207
-	 *  mail => bool
1208
-	 * ]
1209
-	 *
1210
-	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1211
-	 * [
1212
-	 *  users  => ['user1', 'user2', 'user4'],
1213
-	 *  remote => bool,
1214
-	 *  public => bool
1215
-	 *  mail => bool
1216
-	 * ]
1217
-	 *
1218
-	 * This is required for encryption/activity
1219
-	 *
1220
-	 * @param \OCP\Files\Node $path
1221
-	 * @param bool $recursive Should we check all parent folders as well
1222
-	 * @param bool $currentAccess Should the user have currently access to the file
1223
-	 * @return array
1224
-	 */
1225
-	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1226
-		$owner = $path->getOwner()->getUID();
1227
-
1228
-		if ($currentAccess) {
1229
-			$al = ['users' => [], 'remote' => [], 'public' => false];
1230
-		} else {
1231
-			$al = ['users' => [], 'remote' => false, 'public' => false];
1232
-		}
1233
-		if (!$this->userManager->userExists($owner)) {
1234
-			return $al;
1235
-		}
1236
-
1237
-		//Get node for the owner
1238
-		$userFolder = $this->rootFolder->getUserFolder($owner);
1239
-		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1240
-			$path = $userFolder->getById($path->getId())[0];
1241
-		}
1242
-
1243
-		$providers = $this->factory->getAllProviders();
1244
-
1245
-		/** @var Node[] $nodes */
1246
-		$nodes = [];
1247
-
1248
-
1249
-		if ($currentAccess) {
1250
-			$ownerPath = $path->getPath();
1251
-			$ownerPath = explode('/', $ownerPath, 4);
1252
-			if (count($ownerPath) < 4) {
1253
-				$ownerPath = '';
1254
-			} else {
1255
-				$ownerPath = $ownerPath[3];
1256
-			}
1257
-			$al['users'][$owner] = [
1258
-				'node_id' => $path->getId(),
1259
-				'node_path' => '/' . $ownerPath,
1260
-			];
1261
-		} else {
1262
-			$al['users'][] = $owner;
1263
-		}
1264
-
1265
-		// Collect all the shares
1266
-		while ($path->getPath() !== $userFolder->getPath()) {
1267
-			$nodes[] = $path;
1268
-			if (!$recursive) {
1269
-				break;
1270
-			}
1271
-			$path = $path->getParent();
1272
-		}
1273
-
1274
-		foreach ($providers as $provider) {
1275
-			$tmp = $provider->getAccessList($nodes, $currentAccess);
1276
-
1277
-			foreach ($tmp as $k => $v) {
1278
-				if (isset($al[$k])) {
1279
-					if (is_array($al[$k])) {
1280
-						$al[$k] = array_merge($al[$k], $v);
1281
-					} else {
1282
-						$al[$k] = $al[$k] || $v;
1283
-					}
1284
-				} else {
1285
-					$al[$k] = $v;
1286
-				}
1287
-			}
1288
-		}
1289
-
1290
-		return $al;
1291
-	}
1292
-
1293
-	/**
1294
-	 * Create a new share
1295
-	 * @return \OCP\Share\IShare;
1296
-	 */
1297
-	public function newShare() {
1298
-		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1299
-	}
1300
-
1301
-	/**
1302
-	 * Is the share API enabled
1303
-	 *
1304
-	 * @return bool
1305
-	 */
1306
-	public function shareApiEnabled() {
1307
-		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1308
-	}
1309
-
1310
-	/**
1311
-	 * Is public link sharing enabled
1312
-	 *
1313
-	 * @return bool
1314
-	 */
1315
-	public function shareApiAllowLinks() {
1316
-		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1317
-	}
1318
-
1319
-	/**
1320
-	 * Is password on public link requires
1321
-	 *
1322
-	 * @return bool
1323
-	 */
1324
-	public function shareApiLinkEnforcePassword() {
1325
-		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1326
-	}
1327
-
1328
-	/**
1329
-	 * Is default expire date enabled
1330
-	 *
1331
-	 * @return bool
1332
-	 */
1333
-	public function shareApiLinkDefaultExpireDate() {
1334
-		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1335
-	}
1336
-
1337
-	/**
1338
-	 * Is default expire date enforced
1339
-	 *`
1340
-	 * @return bool
1341
-	 */
1342
-	public function shareApiLinkDefaultExpireDateEnforced() {
1343
-		return $this->shareApiLinkDefaultExpireDate() &&
1344
-			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1345
-	}
1346
-
1347
-	/**
1348
-	 * Number of default expire days
1349
-	 *shareApiLinkAllowPublicUpload
1350
-	 * @return int
1351
-	 */
1352
-	public function shareApiLinkDefaultExpireDays() {
1353
-		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1354
-	}
1355
-
1356
-	/**
1357
-	 * Allow public upload on link shares
1358
-	 *
1359
-	 * @return bool
1360
-	 */
1361
-	public function shareApiLinkAllowPublicUpload() {
1362
-		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1363
-	}
1364
-
1365
-	/**
1366
-	 * check if user can only share with group members
1367
-	 * @return bool
1368
-	 */
1369
-	public function shareWithGroupMembersOnly() {
1370
-		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1371
-	}
1372
-
1373
-	/**
1374
-	 * Check if users can share with groups
1375
-	 * @return bool
1376
-	 */
1377
-	public function allowGroupSharing() {
1378
-		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1379
-	}
1380
-
1381
-	/**
1382
-	 * Copied from \OC_Util::isSharingDisabledForUser
1383
-	 *
1384
-	 * TODO: Deprecate fuction from OC_Util
1385
-	 *
1386
-	 * @param string $userId
1387
-	 * @return bool
1388
-	 */
1389
-	public function sharingDisabledForUser($userId) {
1390
-		if ($userId === null) {
1391
-			return false;
1392
-		}
1393
-
1394
-		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1395
-			return $this->sharingDisabledForUsersCache[$userId];
1396
-		}
1397
-
1398
-		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1399
-			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1400
-			$excludedGroups = json_decode($groupsList);
1401
-			if (is_null($excludedGroups)) {
1402
-				$excludedGroups = explode(',', $groupsList);
1403
-				$newValue = json_encode($excludedGroups);
1404
-				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1405
-			}
1406
-			$user = $this->userManager->get($userId);
1407
-			$usersGroups = $this->groupManager->getUserGroupIds($user);
1408
-			if (!empty($usersGroups)) {
1409
-				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1410
-				// if the user is only in groups which are disabled for sharing then
1411
-				// sharing is also disabled for the user
1412
-				if (empty($remainingGroups)) {
1413
-					$this->sharingDisabledForUsersCache[$userId] = true;
1414
-					return true;
1415
-				}
1416
-			}
1417
-		}
1418
-
1419
-		$this->sharingDisabledForUsersCache[$userId] = false;
1420
-		return false;
1421
-	}
1422
-
1423
-	/**
1424
-	 * @inheritdoc
1425
-	 */
1426
-	public function outgoingServer2ServerSharesAllowed() {
1427
-		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1428
-	}
1429
-
1430
-	/**
1431
-	 * @inheritdoc
1432
-	 */
1433
-	public function shareProviderExists($shareType) {
1434
-		try {
1435
-			$this->factory->getProviderForType($shareType);
1436
-		} catch (ProviderException $e) {
1437
-			return false;
1438
-		}
1439
-
1440
-		return true;
1441
-	}
1099
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1100
+            !$this->shareApiLinkAllowPublicUpload()) {
1101
+            $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1102
+        }
1103
+
1104
+        return $share;
1105
+    }
1106
+
1107
+    protected function checkExpireDate($share) {
1108
+        if ($share->getExpirationDate() !== null &&
1109
+            $share->getExpirationDate() <= new \DateTime()) {
1110
+            $this->deleteShare($share);
1111
+            throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
1112
+        }
1113
+
1114
+    }
1115
+
1116
+    /**
1117
+     * Verify the password of a public share
1118
+     *
1119
+     * @param \OCP\Share\IShare $share
1120
+     * @param string $password
1121
+     * @return bool
1122
+     */
1123
+    public function checkPassword(\OCP\Share\IShare $share, $password) {
1124
+        $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1125
+            || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1126
+        if (!$passwordProtected) {
1127
+            //TODO maybe exception?
1128
+            return false;
1129
+        }
1130
+
1131
+        if ($password === null || $share->getPassword() === null) {
1132
+            return false;
1133
+        }
1134
+
1135
+        $newHash = '';
1136
+        if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1137
+            return false;
1138
+        }
1139
+
1140
+        if (!empty($newHash)) {
1141
+            $share->setPassword($newHash);
1142
+            $provider = $this->factory->getProviderForType($share->getShareType());
1143
+            $provider->update($share);
1144
+        }
1145
+
1146
+        return true;
1147
+    }
1148
+
1149
+    /**
1150
+     * @inheritdoc
1151
+     */
1152
+    public function userDeleted($uid) {
1153
+        $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];
1154
+
1155
+        foreach ($types as $type) {
1156
+            try {
1157
+                $provider = $this->factory->getProviderForType($type);
1158
+            } catch (ProviderException $e) {
1159
+                continue;
1160
+            }
1161
+            $provider->userDeleted($uid, $type);
1162
+        }
1163
+    }
1164
+
1165
+    /**
1166
+     * @inheritdoc
1167
+     */
1168
+    public function groupDeleted($gid) {
1169
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1170
+        $provider->groupDeleted($gid);
1171
+    }
1172
+
1173
+    /**
1174
+     * @inheritdoc
1175
+     */
1176
+    public function userDeletedFromGroup($uid, $gid) {
1177
+        $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1178
+        $provider->userDeletedFromGroup($uid, $gid);
1179
+    }
1180
+
1181
+    /**
1182
+     * Get access list to a path. This means
1183
+     * all the users that can access a given path.
1184
+     *
1185
+     * Consider:
1186
+     * -root
1187
+     * |-folder1 (23)
1188
+     *  |-folder2 (32)
1189
+     *   |-fileA (42)
1190
+     *
1191
+     * fileA is shared with user1 and user1@server1
1192
+     * folder2 is shared with group2 (user4 is a member of group2)
1193
+     * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1194
+     *
1195
+     * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1196
+     * [
1197
+     *  users  => [
1198
+     *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1199
+     *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1200
+     *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1201
+     *  ],
1202
+     *  remote => [
1203
+     *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1204
+     *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1205
+     *  ],
1206
+     *  public => bool
1207
+     *  mail => bool
1208
+     * ]
1209
+     *
1210
+     * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1211
+     * [
1212
+     *  users  => ['user1', 'user2', 'user4'],
1213
+     *  remote => bool,
1214
+     *  public => bool
1215
+     *  mail => bool
1216
+     * ]
1217
+     *
1218
+     * This is required for encryption/activity
1219
+     *
1220
+     * @param \OCP\Files\Node $path
1221
+     * @param bool $recursive Should we check all parent folders as well
1222
+     * @param bool $currentAccess Should the user have currently access to the file
1223
+     * @return array
1224
+     */
1225
+    public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1226
+        $owner = $path->getOwner()->getUID();
1227
+
1228
+        if ($currentAccess) {
1229
+            $al = ['users' => [], 'remote' => [], 'public' => false];
1230
+        } else {
1231
+            $al = ['users' => [], 'remote' => false, 'public' => false];
1232
+        }
1233
+        if (!$this->userManager->userExists($owner)) {
1234
+            return $al;
1235
+        }
1236
+
1237
+        //Get node for the owner
1238
+        $userFolder = $this->rootFolder->getUserFolder($owner);
1239
+        if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1240
+            $path = $userFolder->getById($path->getId())[0];
1241
+        }
1242
+
1243
+        $providers = $this->factory->getAllProviders();
1244
+
1245
+        /** @var Node[] $nodes */
1246
+        $nodes = [];
1247
+
1248
+
1249
+        if ($currentAccess) {
1250
+            $ownerPath = $path->getPath();
1251
+            $ownerPath = explode('/', $ownerPath, 4);
1252
+            if (count($ownerPath) < 4) {
1253
+                $ownerPath = '';
1254
+            } else {
1255
+                $ownerPath = $ownerPath[3];
1256
+            }
1257
+            $al['users'][$owner] = [
1258
+                'node_id' => $path->getId(),
1259
+                'node_path' => '/' . $ownerPath,
1260
+            ];
1261
+        } else {
1262
+            $al['users'][] = $owner;
1263
+        }
1264
+
1265
+        // Collect all the shares
1266
+        while ($path->getPath() !== $userFolder->getPath()) {
1267
+            $nodes[] = $path;
1268
+            if (!$recursive) {
1269
+                break;
1270
+            }
1271
+            $path = $path->getParent();
1272
+        }
1273
+
1274
+        foreach ($providers as $provider) {
1275
+            $tmp = $provider->getAccessList($nodes, $currentAccess);
1276
+
1277
+            foreach ($tmp as $k => $v) {
1278
+                if (isset($al[$k])) {
1279
+                    if (is_array($al[$k])) {
1280
+                        $al[$k] = array_merge($al[$k], $v);
1281
+                    } else {
1282
+                        $al[$k] = $al[$k] || $v;
1283
+                    }
1284
+                } else {
1285
+                    $al[$k] = $v;
1286
+                }
1287
+            }
1288
+        }
1289
+
1290
+        return $al;
1291
+    }
1292
+
1293
+    /**
1294
+     * Create a new share
1295
+     * @return \OCP\Share\IShare;
1296
+     */
1297
+    public function newShare() {
1298
+        return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1299
+    }
1300
+
1301
+    /**
1302
+     * Is the share API enabled
1303
+     *
1304
+     * @return bool
1305
+     */
1306
+    public function shareApiEnabled() {
1307
+        return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1308
+    }
1309
+
1310
+    /**
1311
+     * Is public link sharing enabled
1312
+     *
1313
+     * @return bool
1314
+     */
1315
+    public function shareApiAllowLinks() {
1316
+        return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1317
+    }
1318
+
1319
+    /**
1320
+     * Is password on public link requires
1321
+     *
1322
+     * @return bool
1323
+     */
1324
+    public function shareApiLinkEnforcePassword() {
1325
+        return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1326
+    }
1327
+
1328
+    /**
1329
+     * Is default expire date enabled
1330
+     *
1331
+     * @return bool
1332
+     */
1333
+    public function shareApiLinkDefaultExpireDate() {
1334
+        return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1335
+    }
1336
+
1337
+    /**
1338
+     * Is default expire date enforced
1339
+     *`
1340
+     * @return bool
1341
+     */
1342
+    public function shareApiLinkDefaultExpireDateEnforced() {
1343
+        return $this->shareApiLinkDefaultExpireDate() &&
1344
+            $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1345
+    }
1346
+
1347
+    /**
1348
+     * Number of default expire days
1349
+     *shareApiLinkAllowPublicUpload
1350
+     * @return int
1351
+     */
1352
+    public function shareApiLinkDefaultExpireDays() {
1353
+        return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1354
+    }
1355
+
1356
+    /**
1357
+     * Allow public upload on link shares
1358
+     *
1359
+     * @return bool
1360
+     */
1361
+    public function shareApiLinkAllowPublicUpload() {
1362
+        return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1363
+    }
1364
+
1365
+    /**
1366
+     * check if user can only share with group members
1367
+     * @return bool
1368
+     */
1369
+    public function shareWithGroupMembersOnly() {
1370
+        return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1371
+    }
1372
+
1373
+    /**
1374
+     * Check if users can share with groups
1375
+     * @return bool
1376
+     */
1377
+    public function allowGroupSharing() {
1378
+        return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1379
+    }
1380
+
1381
+    /**
1382
+     * Copied from \OC_Util::isSharingDisabledForUser
1383
+     *
1384
+     * TODO: Deprecate fuction from OC_Util
1385
+     *
1386
+     * @param string $userId
1387
+     * @return bool
1388
+     */
1389
+    public function sharingDisabledForUser($userId) {
1390
+        if ($userId === null) {
1391
+            return false;
1392
+        }
1393
+
1394
+        if (isset($this->sharingDisabledForUsersCache[$userId])) {
1395
+            return $this->sharingDisabledForUsersCache[$userId];
1396
+        }
1397
+
1398
+        if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1399
+            $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1400
+            $excludedGroups = json_decode($groupsList);
1401
+            if (is_null($excludedGroups)) {
1402
+                $excludedGroups = explode(',', $groupsList);
1403
+                $newValue = json_encode($excludedGroups);
1404
+                $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1405
+            }
1406
+            $user = $this->userManager->get($userId);
1407
+            $usersGroups = $this->groupManager->getUserGroupIds($user);
1408
+            if (!empty($usersGroups)) {
1409
+                $remainingGroups = array_diff($usersGroups, $excludedGroups);
1410
+                // if the user is only in groups which are disabled for sharing then
1411
+                // sharing is also disabled for the user
1412
+                if (empty($remainingGroups)) {
1413
+                    $this->sharingDisabledForUsersCache[$userId] = true;
1414
+                    return true;
1415
+                }
1416
+            }
1417
+        }
1418
+
1419
+        $this->sharingDisabledForUsersCache[$userId] = false;
1420
+        return false;
1421
+    }
1422
+
1423
+    /**
1424
+     * @inheritdoc
1425
+     */
1426
+    public function outgoingServer2ServerSharesAllowed() {
1427
+        return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1428
+    }
1429
+
1430
+    /**
1431
+     * @inheritdoc
1432
+     */
1433
+    public function shareProviderExists($shareType) {
1434
+        try {
1435
+            $this->factory->getProviderForType($shareType);
1436
+        } catch (ProviderException $e) {
1437
+            return false;
1438
+        }
1439
+
1440
+        return true;
1441
+    }
1442 1442
 
1443 1443
 }
Please login to merge, or discard this patch.