Passed
Push — master ( f6daf1...00cb8e )
by Morris
12:11 queued 12s
created
apps/files_external/lib/Service/UserTrait.php 1 patch
Indentation   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -31,45 +31,45 @@
 block discarded – undo
31 31
  */
32 32
 trait UserTrait {
33 33
 
34
-	/** @var IUserSession */
35
-	protected $userSession;
34
+    /** @var IUserSession */
35
+    protected $userSession;
36 36
 
37
-	/**
38
-	 * User override
39
-	 *
40
-	 * @var IUser|null
41
-	 */
42
-	private $user = null;
37
+    /**
38
+     * User override
39
+     *
40
+     * @var IUser|null
41
+     */
42
+    private $user = null;
43 43
 
44
-	/**
45
-	 * @return IUser|null
46
-	 */
47
-	protected function getUser() {
48
-		if ($this->user) {
49
-			return $this->user;
50
-		}
51
-		return $this->userSession->getUser();
52
-	}
44
+    /**
45
+     * @return IUser|null
46
+     */
47
+    protected function getUser() {
48
+        if ($this->user) {
49
+            return $this->user;
50
+        }
51
+        return $this->userSession->getUser();
52
+    }
53 53
 
54
-	/**
55
-	 * Override the user from the session
56
-	 * Unset with ->resetUser() when finished!
57
-	 *
58
-	 * @param IUser $user
59
-	 * @return self
60
-	 */
61
-	public function setUser(IUser $user) {
62
-		$this->user = $user;
63
-		return $this;
64
-	}
54
+    /**
55
+     * Override the user from the session
56
+     * Unset with ->resetUser() when finished!
57
+     *
58
+     * @param IUser $user
59
+     * @return self
60
+     */
61
+    public function setUser(IUser $user) {
62
+        $this->user = $user;
63
+        return $this;
64
+    }
65 65
 
66
-	/**
67
-	 * Reset the user override
68
-	 *
69
-	 * @return self
70
-	 */
71
-	public function resetUser() {
72
-		$this->user = null;
73
-		return $this;
74
-	}
66
+    /**
67
+     * Reset the user override
68
+     *
69
+     * @return self
70
+     */
71
+    public function resetUser() {
72
+        $this->user = null;
73
+        return $this;
74
+    }
75 75
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Command/ListCommand.php 1 patch
Indentation   +211 added lines, -211 removed lines patch added patch discarded remove patch
@@ -39,239 +39,239 @@
 block discarded – undo
39 39
 use Symfony\Component\Console\Output\OutputInterface;
40 40
 
41 41
 class ListCommand extends Base {
42
-	/**
43
-	 * @var GlobalStoragesService
44
-	 */
45
-	protected $globalService;
42
+    /**
43
+     * @var GlobalStoragesService
44
+     */
45
+    protected $globalService;
46 46
 
47
-	/**
48
-	 * @var UserStoragesService
49
-	 */
50
-	protected $userService;
47
+    /**
48
+     * @var UserStoragesService
49
+     */
50
+    protected $userService;
51 51
 
52
-	/**
53
-	 * @var IUserSession
54
-	 */
55
-	protected $userSession;
52
+    /**
53
+     * @var IUserSession
54
+     */
55
+    protected $userSession;
56 56
 
57
-	/**
58
-	 * @var IUserManager
59
-	 */
60
-	protected $userManager;
57
+    /**
58
+     * @var IUserManager
59
+     */
60
+    protected $userManager;
61 61
 
62
-	public const ALL = -1;
62
+    public const ALL = -1;
63 63
 
64
-	public function __construct(GlobalStoragesService $globalService, UserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) {
65
-		parent::__construct();
66
-		$this->globalService = $globalService;
67
-		$this->userService = $userService;
68
-		$this->userSession = $userSession;
69
-		$this->userManager = $userManager;
70
-	}
64
+    public function __construct(GlobalStoragesService $globalService, UserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) {
65
+        parent::__construct();
66
+        $this->globalService = $globalService;
67
+        $this->userService = $userService;
68
+        $this->userSession = $userSession;
69
+        $this->userManager = $userManager;
70
+    }
71 71
 
72
-	protected function configure() {
73
-		$this
74
-			->setName('files_external:list')
75
-			->setDescription('List configured admin or personal mounts')
76
-			->addArgument(
77
-				'user_id',
78
-				InputArgument::OPTIONAL,
79
-				'user id to list the personal mounts for, if no user is provided admin mounts will be listed'
80
-			)->addOption(
81
-				'show-password',
82
-				'',
83
-				InputOption::VALUE_NONE,
84
-				'show passwords and secrets'
85
-			)->addOption(
86
-				'full',
87
-				null,
88
-				InputOption::VALUE_NONE,
89
-				'don\'t truncate long values in table output'
90
-			)->addOption(
91
-				'all',
92
-				'a',
93
-				InputOption::VALUE_NONE,
94
-				'show both system wide mounts and all personal mounts'
95
-			);
96
-		parent::configure();
97
-	}
72
+    protected function configure() {
73
+        $this
74
+            ->setName('files_external:list')
75
+            ->setDescription('List configured admin or personal mounts')
76
+            ->addArgument(
77
+                'user_id',
78
+                InputArgument::OPTIONAL,
79
+                'user id to list the personal mounts for, if no user is provided admin mounts will be listed'
80
+            )->addOption(
81
+                'show-password',
82
+                '',
83
+                InputOption::VALUE_NONE,
84
+                'show passwords and secrets'
85
+            )->addOption(
86
+                'full',
87
+                null,
88
+                InputOption::VALUE_NONE,
89
+                'don\'t truncate long values in table output'
90
+            )->addOption(
91
+                'all',
92
+                'a',
93
+                InputOption::VALUE_NONE,
94
+                'show both system wide mounts and all personal mounts'
95
+            );
96
+        parent::configure();
97
+    }
98 98
 
99
-	protected function execute(InputInterface $input, OutputInterface $output): int {
100
-		/** @var StorageConfig[] $mounts */
101
-		if ($input->getOption('all')) {
102
-			$mounts = $this->globalService->getStorageForAllUsers();
103
-			$userId = self::ALL;
104
-		} else {
105
-			$userId = $input->getArgument('user_id');
106
-			$storageService = $this->getStorageService($userId);
107
-			$mounts = $storageService->getAllStorages();
108
-		}
99
+    protected function execute(InputInterface $input, OutputInterface $output): int {
100
+        /** @var StorageConfig[] $mounts */
101
+        if ($input->getOption('all')) {
102
+            $mounts = $this->globalService->getStorageForAllUsers();
103
+            $userId = self::ALL;
104
+        } else {
105
+            $userId = $input->getArgument('user_id');
106
+            $storageService = $this->getStorageService($userId);
107
+            $mounts = $storageService->getAllStorages();
108
+        }
109 109
 
110
-		$this->listMounts($userId, $mounts, $input, $output);
111
-		return 0;
112
-	}
110
+        $this->listMounts($userId, $mounts, $input, $output);
111
+        return 0;
112
+    }
113 113
 
114
-	/**
115
-	 * @param string $userId
116
-	 * @param StorageConfig[] $mounts
117
-	 * @param InputInterface $input
118
-	 * @param OutputInterface $output
119
-	 */
120
-	public function listMounts($userId, array $mounts, InputInterface $input, OutputInterface $output) {
121
-		$outputType = $input->getOption('output');
122
-		if (count($mounts) === 0) {
123
-			if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
124
-				$output->writeln('[]');
125
-			} else {
126
-				if ($userId === self::ALL) {
127
-					$output->writeln("<info>No mounts configured</info>");
128
-				} elseif ($userId) {
129
-					$output->writeln("<info>No mounts configured by $userId</info>");
130
-				} else {
131
-					$output->writeln("<info>No admin mounts configured</info>");
132
-				}
133
-			}
134
-			return;
135
-		}
114
+    /**
115
+     * @param string $userId
116
+     * @param StorageConfig[] $mounts
117
+     * @param InputInterface $input
118
+     * @param OutputInterface $output
119
+     */
120
+    public function listMounts($userId, array $mounts, InputInterface $input, OutputInterface $output) {
121
+        $outputType = $input->getOption('output');
122
+        if (count($mounts) === 0) {
123
+            if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
124
+                $output->writeln('[]');
125
+            } else {
126
+                if ($userId === self::ALL) {
127
+                    $output->writeln("<info>No mounts configured</info>");
128
+                } elseif ($userId) {
129
+                    $output->writeln("<info>No mounts configured by $userId</info>");
130
+                } else {
131
+                    $output->writeln("<info>No admin mounts configured</info>");
132
+                }
133
+            }
134
+            return;
135
+        }
136 136
 
137
-		$headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options'];
137
+        $headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options'];
138 138
 
139
-		if (!$userId || $userId === self::ALL) {
140
-			$headers[] = 'Applicable Users';
141
-			$headers[] = 'Applicable Groups';
142
-		}
143
-		if ($userId === self::ALL) {
144
-			$headers[] = 'Type';
145
-		}
139
+        if (!$userId || $userId === self::ALL) {
140
+            $headers[] = 'Applicable Users';
141
+            $headers[] = 'Applicable Groups';
142
+        }
143
+        if ($userId === self::ALL) {
144
+            $headers[] = 'Type';
145
+        }
146 146
 
147
-		if (!$input->getOption('show-password')) {
148
-			$hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key'];
149
-			foreach ($mounts as $mount) {
150
-				$config = $mount->getBackendOptions();
151
-				foreach ($config as $key => $value) {
152
-					if (in_array($key, $hideKeys)) {
153
-						$mount->setBackendOption($key, '***');
154
-					}
155
-				}
156
-			}
157
-		}
147
+        if (!$input->getOption('show-password')) {
148
+            $hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key'];
149
+            foreach ($mounts as $mount) {
150
+                $config = $mount->getBackendOptions();
151
+                foreach ($config as $key => $value) {
152
+                    if (in_array($key, $hideKeys)) {
153
+                        $mount->setBackendOption($key, '***');
154
+                    }
155
+                }
156
+            }
157
+        }
158 158
 
159
-		if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
160
-			$keys = array_map(function ($header) {
161
-				return strtolower(str_replace(' ', '_', $header));
162
-			}, $headers);
159
+        if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
160
+            $keys = array_map(function ($header) {
161
+                return strtolower(str_replace(' ', '_', $header));
162
+            }, $headers);
163 163
 
164
-			$pairs = array_map(function (StorageConfig $config) use ($keys, $userId) {
165
-				$values = [
166
-					$config->getId(),
167
-					$config->getMountPoint(),
168
-					$config->getBackend()->getStorageClass(),
169
-					$config->getAuthMechanism()->getIdentifier(),
170
-					$config->getBackendOptions(),
171
-					$config->getMountOptions()
172
-				];
173
-				if (!$userId || $userId === self::ALL) {
174
-					$values[] = $config->getApplicableUsers();
175
-					$values[] = $config->getApplicableGroups();
176
-				}
177
-				if ($userId === self::ALL) {
178
-					$values[] = $config->getType() === StorageConfig::MOUNT_TYPE_ADMIN ? 'admin' : 'personal';
179
-				}
164
+            $pairs = array_map(function (StorageConfig $config) use ($keys, $userId) {
165
+                $values = [
166
+                    $config->getId(),
167
+                    $config->getMountPoint(),
168
+                    $config->getBackend()->getStorageClass(),
169
+                    $config->getAuthMechanism()->getIdentifier(),
170
+                    $config->getBackendOptions(),
171
+                    $config->getMountOptions()
172
+                ];
173
+                if (!$userId || $userId === self::ALL) {
174
+                    $values[] = $config->getApplicableUsers();
175
+                    $values[] = $config->getApplicableGroups();
176
+                }
177
+                if ($userId === self::ALL) {
178
+                    $values[] = $config->getType() === StorageConfig::MOUNT_TYPE_ADMIN ? 'admin' : 'personal';
179
+                }
180 180
 
181
-				return array_combine($keys, $values);
182
-			}, $mounts);
183
-			if ($outputType === self::OUTPUT_FORMAT_JSON) {
184
-				$output->writeln(json_encode(array_values($pairs)));
185
-			} else {
186
-				$output->writeln(json_encode(array_values($pairs), JSON_PRETTY_PRINT));
187
-			}
188
-		} else {
189
-			$full = $input->getOption('full');
190
-			$defaultMountOptions = [
191
-				'encrypt' => true,
192
-				'previews' => true,
193
-				'filesystem_check_changes' => 1,
194
-				'enable_sharing' => false,
195
-				'encoding_compatibility' => false,
196
-				'readonly' => false,
197
-			];
198
-			$rows = array_map(function (StorageConfig $config) use ($userId, $defaultMountOptions, $full) {
199
-				$storageConfig = $config->getBackendOptions();
200
-				$keys = array_keys($storageConfig);
201
-				$values = array_values($storageConfig);
181
+                return array_combine($keys, $values);
182
+            }, $mounts);
183
+            if ($outputType === self::OUTPUT_FORMAT_JSON) {
184
+                $output->writeln(json_encode(array_values($pairs)));
185
+            } else {
186
+                $output->writeln(json_encode(array_values($pairs), JSON_PRETTY_PRINT));
187
+            }
188
+        } else {
189
+            $full = $input->getOption('full');
190
+            $defaultMountOptions = [
191
+                'encrypt' => true,
192
+                'previews' => true,
193
+                'filesystem_check_changes' => 1,
194
+                'enable_sharing' => false,
195
+                'encoding_compatibility' => false,
196
+                'readonly' => false,
197
+            ];
198
+            $rows = array_map(function (StorageConfig $config) use ($userId, $defaultMountOptions, $full) {
199
+                $storageConfig = $config->getBackendOptions();
200
+                $keys = array_keys($storageConfig);
201
+                $values = array_values($storageConfig);
202 202
 
203
-				if (!$full) {
204
-					$values = array_map(function ($value) {
205
-						if (is_string($value) && strlen($value) > 32) {
206
-							return substr($value, 0, 6) . '...' . substr($value, -6, 6);
207
-						} else {
208
-							return $value;
209
-						}
210
-					}, $values);
211
-				}
203
+                if (!$full) {
204
+                    $values = array_map(function ($value) {
205
+                        if (is_string($value) && strlen($value) > 32) {
206
+                            return substr($value, 0, 6) . '...' . substr($value, -6, 6);
207
+                        } else {
208
+                            return $value;
209
+                        }
210
+                    }, $values);
211
+                }
212 212
 
213
-				$configStrings = array_map(function ($key, $value) {
214
-					return $key . ': ' . json_encode($value);
215
-				}, $keys, $values);
216
-				$configString = implode(', ', $configStrings);
213
+                $configStrings = array_map(function ($key, $value) {
214
+                    return $key . ': ' . json_encode($value);
215
+                }, $keys, $values);
216
+                $configString = implode(', ', $configStrings);
217 217
 
218
-				$mountOptions = $config->getMountOptions();
219
-				// hide defaults
220
-				foreach ($mountOptions as $key => $value) {
221
-					if ($value === $defaultMountOptions[$key]) {
222
-						unset($mountOptions[$key]);
223
-					}
224
-				}
225
-				$keys = array_keys($mountOptions);
226
-				$values = array_values($mountOptions);
218
+                $mountOptions = $config->getMountOptions();
219
+                // hide defaults
220
+                foreach ($mountOptions as $key => $value) {
221
+                    if ($value === $defaultMountOptions[$key]) {
222
+                        unset($mountOptions[$key]);
223
+                    }
224
+                }
225
+                $keys = array_keys($mountOptions);
226
+                $values = array_values($mountOptions);
227 227
 
228
-				$optionsStrings = array_map(function ($key, $value) {
229
-					return $key . ': ' . json_encode($value);
230
-				}, $keys, $values);
231
-				$optionsString = implode(', ', $optionsStrings);
228
+                $optionsStrings = array_map(function ($key, $value) {
229
+                    return $key . ': ' . json_encode($value);
230
+                }, $keys, $values);
231
+                $optionsString = implode(', ', $optionsStrings);
232 232
 
233
-				$values = [
234
-					$config->getId(),
235
-					$config->getMountPoint(),
236
-					$config->getBackend()->getText(),
237
-					$config->getAuthMechanism()->getText(),
238
-					$configString,
239
-					$optionsString
240
-				];
233
+                $values = [
234
+                    $config->getId(),
235
+                    $config->getMountPoint(),
236
+                    $config->getBackend()->getText(),
237
+                    $config->getAuthMechanism()->getText(),
238
+                    $configString,
239
+                    $optionsString
240
+                ];
241 241
 
242
-				if (!$userId || $userId === self::ALL) {
243
-					$applicableUsers = implode(', ', $config->getApplicableUsers());
244
-					$applicableGroups = implode(', ', $config->getApplicableGroups());
245
-					if ($applicableUsers === '' && $applicableGroups === '') {
246
-						$applicableUsers = 'All';
247
-					}
248
-					$values[] = $applicableUsers;
249
-					$values[] = $applicableGroups;
250
-				}
251
-				if ($userId === self::ALL) {
252
-					$values[] = $config->getType() === StorageConfig::MOUNT_TYPE_ADMIN ? 'Admin' : 'Personal';
253
-				}
242
+                if (!$userId || $userId === self::ALL) {
243
+                    $applicableUsers = implode(', ', $config->getApplicableUsers());
244
+                    $applicableGroups = implode(', ', $config->getApplicableGroups());
245
+                    if ($applicableUsers === '' && $applicableGroups === '') {
246
+                        $applicableUsers = 'All';
247
+                    }
248
+                    $values[] = $applicableUsers;
249
+                    $values[] = $applicableGroups;
250
+                }
251
+                if ($userId === self::ALL) {
252
+                    $values[] = $config->getType() === StorageConfig::MOUNT_TYPE_ADMIN ? 'Admin' : 'Personal';
253
+                }
254 254
 
255
-				return $values;
256
-			}, $mounts);
255
+                return $values;
256
+            }, $mounts);
257 257
 
258
-			$table = new Table($output);
259
-			$table->setHeaders($headers);
260
-			$table->setRows($rows);
261
-			$table->render();
262
-		}
263
-	}
258
+            $table = new Table($output);
259
+            $table->setHeaders($headers);
260
+            $table->setRows($rows);
261
+            $table->render();
262
+        }
263
+    }
264 264
 
265
-	protected function getStorageService($userId) {
266
-		if (!empty($userId)) {
267
-			$user = $this->userManager->get($userId);
268
-			if (is_null($user)) {
269
-				throw new NoUserException("user $userId not found");
270
-			}
271
-			$this->userSession->setUser($user);
272
-			return $this->userService;
273
-		} else {
274
-			return $this->globalService;
275
-		}
276
-	}
265
+    protected function getStorageService($userId) {
266
+        if (!empty($userId)) {
267
+            $user = $this->userManager->get($userId);
268
+            if (is_null($user)) {
269
+                throw new NoUserException("user $userId not found");
270
+            }
271
+            $this->userSession->setUser($user);
272
+            return $this->userService;
273
+        } else {
274
+            return $this->globalService;
275
+        }
276
+    }
277 277
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Helper.php 1 patch
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -31,68 +31,68 @@
 block discarded – undo
31 31
 use OC\Files\View;
32 32
 
33 33
 class Helper {
34
-	public static function registerHooks() {
35
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook');
36
-		\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
34
+    public static function registerHooks() {
35
+        \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook');
36
+        \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
37 37
 
38
-		\OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
39
-	}
38
+        \OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
39
+    }
40 40
 
41
-	/**
42
-	 * check if file name already exists and generate unique target
43
-	 *
44
-	 * @param string $path
45
-	 * @param array $excludeList
46
-	 * @param View $view
47
-	 * @return string $path
48
-	 */
49
-	public static function generateUniqueTarget($path, $excludeList, $view) {
50
-		$pathinfo = pathinfo($path);
51
-		$ext = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
52
-		$name = $pathinfo['filename'];
53
-		$dir = $pathinfo['dirname'];
54
-		$i = 2;
55
-		while ($view->file_exists($path) || in_array($path, $excludeList)) {
56
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
57
-			$i++;
58
-		}
41
+    /**
42
+     * check if file name already exists and generate unique target
43
+     *
44
+     * @param string $path
45
+     * @param array $excludeList
46
+     * @param View $view
47
+     * @return string $path
48
+     */
49
+    public static function generateUniqueTarget($path, $excludeList, $view) {
50
+        $pathinfo = pathinfo($path);
51
+        $ext = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
52
+        $name = $pathinfo['filename'];
53
+        $dir = $pathinfo['dirname'];
54
+        $i = 2;
55
+        while ($view->file_exists($path) || in_array($path, $excludeList)) {
56
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
57
+            $i++;
58
+        }
59 59
 
60
-		return $path;
61
-	}
60
+        return $path;
61
+    }
62 62
 
63
-	/**
64
-	 * get default share folder
65
-	 *
66
-	 * @param \OC\Files\View $view
67
-	 * @return string
68
-	 */
69
-	public static function getShareFolder($view = null) {
70
-		if ($view === null) {
71
-			$view = Filesystem::getView();
72
-		}
73
-		$shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
74
-		$shareFolder = Filesystem::normalizePath($shareFolder);
63
+    /**
64
+     * get default share folder
65
+     *
66
+     * @param \OC\Files\View $view
67
+     * @return string
68
+     */
69
+    public static function getShareFolder($view = null) {
70
+        if ($view === null) {
71
+            $view = Filesystem::getView();
72
+        }
73
+        $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
74
+        $shareFolder = Filesystem::normalizePath($shareFolder);
75 75
 
76
-		if (!$view->file_exists($shareFolder)) {
77
-			$dir = '';
78
-			$subdirs = explode('/', $shareFolder);
79
-			foreach ($subdirs as $subdir) {
80
-				$dir = $dir . '/' . $subdir;
81
-				if (!$view->is_dir($dir)) {
82
-					$view->mkdir($dir);
83
-				}
84
-			}
85
-		}
76
+        if (!$view->file_exists($shareFolder)) {
77
+            $dir = '';
78
+            $subdirs = explode('/', $shareFolder);
79
+            foreach ($subdirs as $subdir) {
80
+                $dir = $dir . '/' . $subdir;
81
+                if (!$view->is_dir($dir)) {
82
+                    $view->mkdir($dir);
83
+                }
84
+            }
85
+        }
86 86
 
87
-		return $shareFolder;
88
-	}
87
+        return $shareFolder;
88
+    }
89 89
 
90
-	/**
91
-	 * set default share folder
92
-	 *
93
-	 * @param string $shareFolder
94
-	 */
95
-	public static function setShareFolder($shareFolder) {
96
-		\OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
97
-	}
90
+    /**
91
+     * set default share folder
92
+     *
93
+     * @param string $shareFolder
94
+     */
95
+    public static function setShareFolder($shareFolder) {
96
+        \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
97
+    }
98 98
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareAPIController.php 1 patch
Indentation   +1609 added lines, -1609 removed lines patch added patch discarded remove patch
@@ -78,1616 +78,1616 @@
 block discarded – undo
78 78
  */
79 79
 class ShareAPIController extends OCSController {
80 80
 
81
-	/** @var IManager */
82
-	private $shareManager;
83
-	/** @var IGroupManager */
84
-	private $groupManager;
85
-	/** @var IUserManager */
86
-	private $userManager;
87
-	/** @var IRootFolder */
88
-	private $rootFolder;
89
-	/** @var IURLGenerator */
90
-	private $urlGenerator;
91
-	/** @var string */
92
-	private $currentUser;
93
-	/** @var IL10N */
94
-	private $l;
95
-	/** @var \OCP\Files\Node */
96
-	private $lockedNode;
97
-	/** @var IConfig */
98
-	private $config;
99
-	/** @var IAppManager */
100
-	private $appManager;
101
-	/** @var IServerContainer */
102
-	private $serverContainer;
103
-
104
-	/**
105
-	 * Share20OCS constructor.
106
-	 *
107
-	 * @param string $appName
108
-	 * @param IRequest $request
109
-	 * @param IManager $shareManager
110
-	 * @param IGroupManager $groupManager
111
-	 * @param IUserManager $userManager
112
-	 * @param IRootFolder $rootFolder
113
-	 * @param IURLGenerator $urlGenerator
114
-	 * @param string $userId
115
-	 * @param IL10N $l10n
116
-	 * @param IConfig $config
117
-	 * @param IAppManager $appManager
118
-	 * @param IServerContainer $serverContainer
119
-	 */
120
-	public function __construct(
121
-		string $appName,
122
-		IRequest $request,
123
-		IManager $shareManager,
124
-		IGroupManager $groupManager,
125
-		IUserManager $userManager,
126
-		IRootFolder $rootFolder,
127
-		IURLGenerator $urlGenerator,
128
-		string $userId = null,
129
-		IL10N $l10n,
130
-		IConfig $config,
131
-		IAppManager $appManager,
132
-		IServerContainer $serverContainer
133
-	) {
134
-		parent::__construct($appName, $request);
135
-
136
-		$this->shareManager = $shareManager;
137
-		$this->userManager = $userManager;
138
-		$this->groupManager = $groupManager;
139
-		$this->request = $request;
140
-		$this->rootFolder = $rootFolder;
141
-		$this->urlGenerator = $urlGenerator;
142
-		$this->currentUser = $userId;
143
-		$this->l = $l10n;
144
-		$this->config = $config;
145
-		$this->appManager = $appManager;
146
-		$this->serverContainer = $serverContainer;
147
-	}
148
-
149
-	/**
150
-	 * Convert an IShare to an array for OCS output
151
-	 *
152
-	 * @param \OCP\Share\IShare $share
153
-	 * @param Node|null $recipientNode
154
-	 * @return array
155
-	 * @throws NotFoundException In case the node can't be resolved.
156
-	 *
157
-	 * @suppress PhanUndeclaredClassMethod
158
-	 */
159
-	protected function formatShare(IShare $share, Node $recipientNode = null): array {
160
-		$sharedBy = $this->userManager->get($share->getSharedBy());
161
-		$shareOwner = $this->userManager->get($share->getShareOwner());
162
-
163
-		$result = [
164
-			'id' => $share->getId(),
165
-			'share_type' => $share->getShareType(),
166
-			'uid_owner' => $share->getSharedBy(),
167
-			'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
168
-			// recipient permissions
169
-			'permissions' => $share->getPermissions(),
170
-			// current user permissions on this share
171
-			'can_edit' => $this->canEditShare($share),
172
-			'can_delete' => $this->canDeleteShare($share),
173
-			'stime' => $share->getShareTime()->getTimestamp(),
174
-			'parent' => null,
175
-			'expiration' => null,
176
-			'token' => null,
177
-			'uid_file_owner' => $share->getShareOwner(),
178
-			'note' => $share->getNote(),
179
-			'label' => $share->getLabel(),
180
-			'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
181
-		];
182
-
183
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
184
-		if ($recipientNode) {
185
-			$node = $recipientNode;
186
-		} else {
187
-			$nodes = $userFolder->getById($share->getNodeId());
188
-			if (empty($nodes)) {
189
-				// fallback to guessing the path
190
-				$node = $userFolder->get($share->getTarget());
191
-				if ($node === null || $share->getTarget() === '') {
192
-					throw new NotFoundException();
193
-				}
194
-			} else {
195
-				$node = reset($nodes);
196
-			}
197
-		}
198
-
199
-		$result['path'] = $userFolder->getRelativePath($node->getPath());
200
-		if ($node instanceof Folder) {
201
-			$result['item_type'] = 'folder';
202
-		} else {
203
-			$result['item_type'] = 'file';
204
-		}
205
-
206
-		$result['mimetype'] = $node->getMimetype();
207
-		$result['storage_id'] = $node->getStorage()->getId();
208
-		$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
209
-		$result['item_source'] = $node->getId();
210
-		$result['file_source'] = $node->getId();
211
-		$result['file_parent'] = $node->getParent()->getId();
212
-		$result['file_target'] = $share->getTarget();
213
-
214
-		$expiration = $share->getExpirationDate();
215
-		if ($expiration !== null) {
216
-			$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
217
-		}
218
-
219
-		if ($share->getShareType() === IShare::TYPE_USER) {
220
-			$sharedWith = $this->userManager->get($share->getSharedWith());
221
-			$result['share_with'] = $share->getSharedWith();
222
-			$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
223
-		} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
224
-			$group = $this->groupManager->get($share->getSharedWith());
225
-			$result['share_with'] = $share->getSharedWith();
226
-			$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
227
-		} elseif ($share->getShareType() === IShare::TYPE_LINK) {
228
-
229
-			// "share_with" and "share_with_displayname" for passwords of link
230
-			// shares was deprecated in Nextcloud 15, use "password" instead.
231
-			$result['share_with'] = $share->getPassword();
232
-			$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
233
-
234
-			$result['password'] = $share->getPassword();
235
-
236
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
237
-
238
-			$result['token'] = $share->getToken();
239
-			$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
240
-		} elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
241
-			$result['share_with'] = $share->getSharedWith();
242
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
243
-			$result['token'] = $share->getToken();
244
-		} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
245
-			$result['share_with'] = $share->getSharedWith();
246
-			$result['password'] = $share->getPassword();
247
-			$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
248
-			$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
249
-			$result['token'] = $share->getToken();
250
-		} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
251
-			// getSharedWith() returns either "name (type, owner)" or
252
-			// "name (type, owner) [id]", depending on the Circles app version.
253
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
254
-
255
-			$result['share_with_displayname'] = $share->getSharedWithDisplayName();
256
-			if (empty($result['share_with_displayname'])) {
257
-				$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
258
-				$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
259
-			}
260
-
261
-			$result['share_with_avatar'] = $share->getSharedWithAvatar();
262
-
263
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
264
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
265
-			if (is_bool($shareWithLength)) {
266
-				$shareWithLength = -1;
267
-			}
268
-			$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
269
-		} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
270
-			$result['share_with'] = $share->getSharedWith();
271
-			$result['share_with_displayname'] = '';
272
-
273
-			try {
274
-				$result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
275
-			} catch (QueryException $e) {
276
-			}
277
-		}
278
-
279
-
280
-		$result['mail_send'] = $share->getMailSend() ? 1 : 0;
281
-		$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
282
-
283
-		return $result;
284
-	}
285
-
286
-	/**
287
-	 * Check if one of the users address books knows the exact property, if
288
-	 * yes we return the full name.
289
-	 *
290
-	 * @param string $query
291
-	 * @param string $property
292
-	 * @return string
293
-	 */
294
-	private function getDisplayNameFromAddressBook(string $query, string $property): string {
295
-		// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
296
-		$result = \OC::$server->getContactsManager()->search($query, [$property]);
297
-		foreach ($result as $r) {
298
-			foreach ($r[$property] as $value) {
299
-				if ($value === $query) {
300
-					return $r['FN'];
301
-				}
302
-			}
303
-		}
304
-
305
-		return $query;
306
-	}
307
-
308
-	/**
309
-	 * Get a specific share by id
310
-	 *
311
-	 * @NoAdminRequired
312
-	 *
313
-	 * @param string $id
314
-	 * @return DataResponse
315
-	 * @throws OCSNotFoundException
316
-	 */
317
-	public function getShare(string $id): DataResponse {
318
-		try {
319
-			$share = $this->getShareById($id);
320
-		} catch (ShareNotFound $e) {
321
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
322
-		}
323
-
324
-		try {
325
-			if ($this->canAccessShare($share)) {
326
-				$share = $this->formatShare($share);
327
-				return new DataResponse([$share]);
328
-			}
329
-		} catch (NotFoundException $e) {
330
-			// Fall trough
331
-		}
332
-
333
-		throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
334
-	}
335
-
336
-	/**
337
-	 * Delete a share
338
-	 *
339
-	 * @NoAdminRequired
340
-	 *
341
-	 * @param string $id
342
-	 * @return DataResponse
343
-	 * @throws OCSNotFoundException
344
-	 */
345
-	public function deleteShare(string $id): DataResponse {
346
-		try {
347
-			$share = $this->getShareById($id);
348
-		} catch (ShareNotFound $e) {
349
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
350
-		}
351
-
352
-		try {
353
-			$this->lock($share->getNode());
354
-		} catch (LockedException $e) {
355
-			throw new OCSNotFoundException($this->l->t('Could not delete share'));
356
-		}
357
-
358
-		if (!$this->canAccessShare($share)) {
359
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
360
-		}
361
-
362
-		// if it's a group share or a room share
363
-		// we don't delete the share, but only the
364
-		// mount point. Allowing it to be restored
365
-		// from the deleted shares
366
-		if ($this->canDeleteShareFromSelf($share)) {
367
-			$this->shareManager->deleteFromSelf($share, $this->currentUser);
368
-		} else {
369
-			if (!$this->canDeleteShare($share)) {
370
-				throw new OCSForbiddenException($this->l->t('Could not delete share'));
371
-			}
372
-
373
-			$this->shareManager->deleteShare($share);
374
-		}
375
-
376
-		return new DataResponse();
377
-	}
378
-
379
-	/**
380
-	 * @NoAdminRequired
381
-	 *
382
-	 * @param string $path
383
-	 * @param int $permissions
384
-	 * @param int $shareType
385
-	 * @param string $shareWith
386
-	 * @param string $publicUpload
387
-	 * @param string $password
388
-	 * @param string $sendPasswordByTalk
389
-	 * @param string $expireDate
390
-	 * @param string $label
391
-	 *
392
-	 * @return DataResponse
393
-	 * @throws NotFoundException
394
-	 * @throws OCSBadRequestException
395
-	 * @throws OCSException
396
-	 * @throws OCSForbiddenException
397
-	 * @throws OCSNotFoundException
398
-	 * @throws InvalidPathException
399
-	 * @suppress PhanUndeclaredClassMethod
400
-	 */
401
-	public function createShare(
402
-		string $path = null,
403
-		int $permissions = null,
404
-		int $shareType = -1,
405
-		string $shareWith = null,
406
-		string $publicUpload = 'false',
407
-		string $password = '',
408
-		string $sendPasswordByTalk = null,
409
-		string $expireDate = '',
410
-		string $label = ''
411
-	): DataResponse {
412
-		$share = $this->shareManager->newShare();
413
-
414
-		if ($permissions === null) {
415
-			$permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL);
416
-		}
417
-
418
-		// Verify path
419
-		if ($path === null) {
420
-			throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
421
-		}
422
-
423
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
424
-		try {
425
-			$path = $userFolder->get($path);
426
-		} catch (NotFoundException $e) {
427
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
428
-		}
429
-
430
-		$share->setNode($path);
431
-
432
-		try {
433
-			$this->lock($share->getNode());
434
-		} catch (LockedException $e) {
435
-			throw new OCSNotFoundException($this->l->t('Could not create share'));
436
-		}
437
-
438
-		if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
439
-			throw new OCSNotFoundException($this->l->t('invalid permissions'));
440
-		}
441
-
442
-		// Shares always require read permissions
443
-		$permissions |= Constants::PERMISSION_READ;
444
-
445
-		if ($path instanceof \OCP\Files\File) {
446
-			// Single file shares should never have delete or create permissions
447
-			$permissions &= ~Constants::PERMISSION_DELETE;
448
-			$permissions &= ~Constants::PERMISSION_CREATE;
449
-		}
450
-
451
-		/**
452
-		 * Hack for https://github.com/owncloud/core/issues/22587
453
-		 * We check the permissions via webdav. But the permissions of the mount point
454
-		 * do not equal the share permissions. Here we fix that for federated mounts.
455
-		 */
456
-		if ($path->getStorage()->instanceOfStorage(Storage::class)) {
457
-			$permissions &= ~($permissions & ~$path->getPermissions());
458
-		}
459
-
460
-		if ($shareType === IShare::TYPE_USER) {
461
-			// Valid user is required to share
462
-			if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
463
-				throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
464
-			}
465
-			$share->setSharedWith($shareWith);
466
-			$share->setPermissions($permissions);
467
-		} elseif ($shareType === IShare::TYPE_GROUP) {
468
-			if (!$this->shareManager->allowGroupSharing()) {
469
-				throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
470
-			}
471
-
472
-			// Valid group is required to share
473
-			if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
474
-				throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
475
-			}
476
-			$share->setSharedWith($shareWith);
477
-			$share->setPermissions($permissions);
478
-		} elseif ($shareType === IShare::TYPE_LINK
479
-			|| $shareType === IShare::TYPE_EMAIL) {
480
-
481
-			// Can we even share links?
482
-			if (!$this->shareManager->shareApiAllowLinks()) {
483
-				throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
484
-			}
485
-
486
-			if ($publicUpload === 'true') {
487
-				// Check if public upload is allowed
488
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
489
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
490
-				}
491
-
492
-				// Public upload can only be set for folders
493
-				if ($path instanceof \OCP\Files\File) {
494
-					throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
495
-				}
496
-
497
-				$permissions = Constants::PERMISSION_READ |
498
-					Constants::PERMISSION_CREATE |
499
-					Constants::PERMISSION_UPDATE |
500
-					Constants::PERMISSION_DELETE;
501
-			} else {
502
-				$permissions = Constants::PERMISSION_READ;
503
-			}
504
-
505
-			// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
506
-			if (($permissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
507
-				$permissions |= Constants::PERMISSION_SHARE;
508
-			}
509
-
510
-			$share->setPermissions($permissions);
511
-
512
-			// Set password
513
-			if ($password !== '') {
514
-				$share->setPassword($password);
515
-			}
516
-
517
-			// Only share by mail have a recipient
518
-			if ($shareType === IShare::TYPE_EMAIL) {
519
-				$share->setSharedWith($shareWith);
520
-			} else {
521
-				// Only link share have a label
522
-				if (!empty($label)) {
523
-					$share->setLabel($label);
524
-				}
525
-			}
526
-
527
-			if ($sendPasswordByTalk === 'true') {
528
-				if (!$this->appManager->isEnabledForUser('spreed')) {
529
-					throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$path->getPath()]));
530
-				}
531
-
532
-				$share->setSendPasswordByTalk(true);
533
-			}
534
-
535
-			//Expire date
536
-			if ($expireDate !== '') {
537
-				try {
538
-					$expireDate = $this->parseDate($expireDate);
539
-					$share->setExpirationDate($expireDate);
540
-				} catch (\Exception $e) {
541
-					throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
542
-				}
543
-			}
544
-		} elseif ($shareType === IShare::TYPE_REMOTE) {
545
-			if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
546
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
547
-			}
548
-
549
-			$share->setSharedWith($shareWith);
550
-			$share->setPermissions($permissions);
551
-		} elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
552
-			if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
553
-				throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
554
-			}
555
-
556
-			$share->setSharedWith($shareWith);
557
-			$share->setPermissions($permissions);
558
-		} elseif ($shareType === IShare::TYPE_CIRCLE) {
559
-			if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
560
-				throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
561
-			}
562
-
563
-			$circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
564
-
565
-			// Valid circle is required to share
566
-			if ($circle === null) {
567
-				throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
568
-			}
569
-			$share->setSharedWith($shareWith);
570
-			$share->setPermissions($permissions);
571
-		} elseif ($shareType === IShare::TYPE_ROOM) {
572
-			try {
573
-				$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate);
574
-			} catch (QueryException $e) {
575
-				throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()]));
576
-			}
577
-		} else {
578
-			throw new OCSBadRequestException($this->l->t('Unknown share type'));
579
-		}
580
-
581
-		$share->setShareType($shareType);
582
-		$share->setSharedBy($this->currentUser);
583
-
584
-		try {
585
-			$share = $this->shareManager->createShare($share);
586
-		} catch (GenericShareException $e) {
587
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
588
-			throw new OCSException($e->getHint(), $code);
589
-		} catch (\Exception $e) {
590
-			throw new OCSForbiddenException($e->getMessage(), $e);
591
-		}
592
-
593
-		$output = $this->formatShare($share);
594
-
595
-		return new DataResponse($output);
596
-	}
597
-
598
-	/**
599
-	 * @param null|Node $node
600
-	 * @param boolean $includeTags
601
-	 *
602
-	 * @return array
603
-	 */
604
-	private function getSharedWithMe($node, bool $includeTags): array {
605
-		$userShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_USER, $node, -1, 0);
606
-		$groupShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_GROUP, $node, -1, 0);
607
-		$circleShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_CIRCLE, $node, -1, 0);
608
-		$roomShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_ROOM, $node, -1, 0);
609
-
610
-		$shares = array_merge($userShares, $groupShares, $circleShares, $roomShares);
611
-
612
-		$filteredShares = array_filter($shares, function (IShare $share) {
613
-			return $share->getShareOwner() !== $this->currentUser;
614
-		});
615
-
616
-		$formatted = [];
617
-		foreach ($filteredShares as $share) {
618
-			if ($this->canAccessShare($share)) {
619
-				try {
620
-					$formatted[] = $this->formatShare($share);
621
-				} catch (NotFoundException $e) {
622
-					// Ignore this share
623
-				}
624
-			}
625
-		}
626
-
627
-		if ($includeTags) {
628
-			$formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
629
-		}
630
-
631
-		return $formatted;
632
-	}
633
-
634
-	/**
635
-	 * @param \OCP\Files\Node $folder
636
-	 *
637
-	 * @return array
638
-	 * @throws OCSBadRequestException
639
-	 * @throws NotFoundException
640
-	 */
641
-	private function getSharesInDir(Node $folder): array {
642
-		if (!($folder instanceof \OCP\Files\Folder)) {
643
-			throw new OCSBadRequestException($this->l->t('Not a directory'));
644
-		}
645
-
646
-		$nodes = $folder->getDirectoryListing();
647
-
648
-		/** @var \OCP\Share\IShare[] $shares */
649
-		$shares = array_reduce($nodes, function ($carry, $node) {
650
-			$carry = array_merge($carry, $this->getAllShares($node, true));
651
-			return $carry;
652
-		}, []);
653
-
654
-		// filter out duplicate shares
655
-		$known = [];
656
-
657
-
658
-		$formatted = $miniFormatted = [];
659
-		$resharingRight = false;
660
-		$known = [];
661
-		foreach ($shares as $share) {
662
-			if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
663
-				continue;
664
-			}
665
-
666
-			try {
667
-				$format = $this->formatShare($share);
668
-
669
-				$known[] = $share->getId();
670
-				$formatted[] = $format;
671
-				if ($share->getSharedBy() === $this->currentUser) {
672
-					$miniFormatted[] = $format;
673
-				}
674
-				if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $folder)) {
675
-					$resharingRight = true;
676
-				}
677
-			} catch (\Exception $e) {
678
-				//Ignore this share
679
-			}
680
-		}
681
-
682
-		if (!$resharingRight) {
683
-			$formatted = $miniFormatted;
684
-		}
685
-
686
-		return $formatted;
687
-	}
688
-
689
-	/**
690
-	 * The getShares function.
691
-	 *
692
-	 * @NoAdminRequired
693
-	 *
694
-	 * @param string $shared_with_me
695
-	 * @param string $reshares
696
-	 * @param string $subfiles
697
-	 * @param string $path
698
-	 *
699
-	 * - Get shares by the current user
700
-	 * - Get shares by the current user and reshares (?reshares=true)
701
-	 * - Get shares with the current user (?shared_with_me=true)
702
-	 * - Get shares for a specific path (?path=...)
703
-	 * - Get all shares in a folder (?subfiles=true&path=..)
704
-	 *
705
-	 * @param string $include_tags
706
-	 *
707
-	 * @return DataResponse
708
-	 * @throws NotFoundException
709
-	 * @throws OCSBadRequestException
710
-	 * @throws OCSNotFoundException
711
-	 */
712
-	public function getShares(
713
-		string $shared_with_me = 'false',
714
-		string $reshares = 'false',
715
-		string $subfiles = 'false',
716
-		string $path = '',
717
-		string $include_tags = 'false'
718
-	): DataResponse {
719
-		$node = null;
720
-		if ($path !== '') {
721
-			$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
722
-			try {
723
-				$node = $userFolder->get($path);
724
-				$this->lock($node);
725
-			} catch (NotFoundException $e) {
726
-				throw new OCSNotFoundException(
727
-					$this->l->t('Wrong path, file/folder doesn\'t exist')
728
-				);
729
-			} catch (LockedException $e) {
730
-				throw new OCSNotFoundException($this->l->t('Could not lock node'));
731
-			}
732
-		}
733
-
734
-		$shares = $this->getFormattedShares(
735
-			$this->currentUser,
736
-			$node,
737
-			($shared_with_me === 'true'),
738
-			($reshares === 'true'),
739
-			($subfiles === 'true'),
740
-			($include_tags === 'true')
741
-		);
742
-
743
-		return new DataResponse($shares);
744
-	}
745
-
746
-
747
-	/**
748
-	 * @param string $viewer
749
-	 * @param Node $node
750
-	 * @param bool $sharedWithMe
751
-	 * @param bool $reShares
752
-	 * @param bool $subFiles
753
-	 * @param bool $includeTags
754
-	 *
755
-	 * @return array
756
-	 * @throws NotFoundException
757
-	 * @throws OCSBadRequestException
758
-	 */
759
-	private function getFormattedShares(
760
-		string $viewer,
761
-		$node = null,
762
-		bool $sharedWithMe = false,
763
-		bool $reShares = false,
764
-		bool $subFiles = false,
765
-		bool $includeTags = false
766
-	): array {
767
-		if ($sharedWithMe) {
768
-			return $this->getSharedWithMe($node, $includeTags);
769
-		}
770
-
771
-		if ($subFiles) {
772
-			return $this->getSharesInDir($node);
773
-		}
774
-
775
-		$shares = $this->getSharesFromNode($viewer, $node, $reShares);
776
-
777
-		$known = $formatted = $miniFormatted = [];
778
-		$resharingRight = false;
779
-		foreach ($shares as $share) {
780
-			try {
781
-				$share->getNode();
782
-			} catch (NotFoundException $e) {
783
-				/*
81
+    /** @var IManager */
82
+    private $shareManager;
83
+    /** @var IGroupManager */
84
+    private $groupManager;
85
+    /** @var IUserManager */
86
+    private $userManager;
87
+    /** @var IRootFolder */
88
+    private $rootFolder;
89
+    /** @var IURLGenerator */
90
+    private $urlGenerator;
91
+    /** @var string */
92
+    private $currentUser;
93
+    /** @var IL10N */
94
+    private $l;
95
+    /** @var \OCP\Files\Node */
96
+    private $lockedNode;
97
+    /** @var IConfig */
98
+    private $config;
99
+    /** @var IAppManager */
100
+    private $appManager;
101
+    /** @var IServerContainer */
102
+    private $serverContainer;
103
+
104
+    /**
105
+     * Share20OCS constructor.
106
+     *
107
+     * @param string $appName
108
+     * @param IRequest $request
109
+     * @param IManager $shareManager
110
+     * @param IGroupManager $groupManager
111
+     * @param IUserManager $userManager
112
+     * @param IRootFolder $rootFolder
113
+     * @param IURLGenerator $urlGenerator
114
+     * @param string $userId
115
+     * @param IL10N $l10n
116
+     * @param IConfig $config
117
+     * @param IAppManager $appManager
118
+     * @param IServerContainer $serverContainer
119
+     */
120
+    public function __construct(
121
+        string $appName,
122
+        IRequest $request,
123
+        IManager $shareManager,
124
+        IGroupManager $groupManager,
125
+        IUserManager $userManager,
126
+        IRootFolder $rootFolder,
127
+        IURLGenerator $urlGenerator,
128
+        string $userId = null,
129
+        IL10N $l10n,
130
+        IConfig $config,
131
+        IAppManager $appManager,
132
+        IServerContainer $serverContainer
133
+    ) {
134
+        parent::__construct($appName, $request);
135
+
136
+        $this->shareManager = $shareManager;
137
+        $this->userManager = $userManager;
138
+        $this->groupManager = $groupManager;
139
+        $this->request = $request;
140
+        $this->rootFolder = $rootFolder;
141
+        $this->urlGenerator = $urlGenerator;
142
+        $this->currentUser = $userId;
143
+        $this->l = $l10n;
144
+        $this->config = $config;
145
+        $this->appManager = $appManager;
146
+        $this->serverContainer = $serverContainer;
147
+    }
148
+
149
+    /**
150
+     * Convert an IShare to an array for OCS output
151
+     *
152
+     * @param \OCP\Share\IShare $share
153
+     * @param Node|null $recipientNode
154
+     * @return array
155
+     * @throws NotFoundException In case the node can't be resolved.
156
+     *
157
+     * @suppress PhanUndeclaredClassMethod
158
+     */
159
+    protected function formatShare(IShare $share, Node $recipientNode = null): array {
160
+        $sharedBy = $this->userManager->get($share->getSharedBy());
161
+        $shareOwner = $this->userManager->get($share->getShareOwner());
162
+
163
+        $result = [
164
+            'id' => $share->getId(),
165
+            'share_type' => $share->getShareType(),
166
+            'uid_owner' => $share->getSharedBy(),
167
+            'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
168
+            // recipient permissions
169
+            'permissions' => $share->getPermissions(),
170
+            // current user permissions on this share
171
+            'can_edit' => $this->canEditShare($share),
172
+            'can_delete' => $this->canDeleteShare($share),
173
+            'stime' => $share->getShareTime()->getTimestamp(),
174
+            'parent' => null,
175
+            'expiration' => null,
176
+            'token' => null,
177
+            'uid_file_owner' => $share->getShareOwner(),
178
+            'note' => $share->getNote(),
179
+            'label' => $share->getLabel(),
180
+            'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
181
+        ];
182
+
183
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
184
+        if ($recipientNode) {
185
+            $node = $recipientNode;
186
+        } else {
187
+            $nodes = $userFolder->getById($share->getNodeId());
188
+            if (empty($nodes)) {
189
+                // fallback to guessing the path
190
+                $node = $userFolder->get($share->getTarget());
191
+                if ($node === null || $share->getTarget() === '') {
192
+                    throw new NotFoundException();
193
+                }
194
+            } else {
195
+                $node = reset($nodes);
196
+            }
197
+        }
198
+
199
+        $result['path'] = $userFolder->getRelativePath($node->getPath());
200
+        if ($node instanceof Folder) {
201
+            $result['item_type'] = 'folder';
202
+        } else {
203
+            $result['item_type'] = 'file';
204
+        }
205
+
206
+        $result['mimetype'] = $node->getMimetype();
207
+        $result['storage_id'] = $node->getStorage()->getId();
208
+        $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
209
+        $result['item_source'] = $node->getId();
210
+        $result['file_source'] = $node->getId();
211
+        $result['file_parent'] = $node->getParent()->getId();
212
+        $result['file_target'] = $share->getTarget();
213
+
214
+        $expiration = $share->getExpirationDate();
215
+        if ($expiration !== null) {
216
+            $result['expiration'] = $expiration->format('Y-m-d 00:00:00');
217
+        }
218
+
219
+        if ($share->getShareType() === IShare::TYPE_USER) {
220
+            $sharedWith = $this->userManager->get($share->getSharedWith());
221
+            $result['share_with'] = $share->getSharedWith();
222
+            $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
223
+        } elseif ($share->getShareType() === IShare::TYPE_GROUP) {
224
+            $group = $this->groupManager->get($share->getSharedWith());
225
+            $result['share_with'] = $share->getSharedWith();
226
+            $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
227
+        } elseif ($share->getShareType() === IShare::TYPE_LINK) {
228
+
229
+            // "share_with" and "share_with_displayname" for passwords of link
230
+            // shares was deprecated in Nextcloud 15, use "password" instead.
231
+            $result['share_with'] = $share->getPassword();
232
+            $result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
233
+
234
+            $result['password'] = $share->getPassword();
235
+
236
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
237
+
238
+            $result['token'] = $share->getToken();
239
+            $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
240
+        } elseif ($share->getShareType() === IShare::TYPE_REMOTE || $share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
241
+            $result['share_with'] = $share->getSharedWith();
242
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
243
+            $result['token'] = $share->getToken();
244
+        } elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
245
+            $result['share_with'] = $share->getSharedWith();
246
+            $result['password'] = $share->getPassword();
247
+            $result['send_password_by_talk'] = $share->getSendPasswordByTalk();
248
+            $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
249
+            $result['token'] = $share->getToken();
250
+        } elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
251
+            // getSharedWith() returns either "name (type, owner)" or
252
+            // "name (type, owner) [id]", depending on the Circles app version.
253
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
254
+
255
+            $result['share_with_displayname'] = $share->getSharedWithDisplayName();
256
+            if (empty($result['share_with_displayname'])) {
257
+                $displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
258
+                $result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
259
+            }
260
+
261
+            $result['share_with_avatar'] = $share->getSharedWithAvatar();
262
+
263
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
264
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
265
+            if (is_bool($shareWithLength)) {
266
+                $shareWithLength = -1;
267
+            }
268
+            $result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
269
+        } elseif ($share->getShareType() === IShare::TYPE_ROOM) {
270
+            $result['share_with'] = $share->getSharedWith();
271
+            $result['share_with_displayname'] = '';
272
+
273
+            try {
274
+                $result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
275
+            } catch (QueryException $e) {
276
+            }
277
+        }
278
+
279
+
280
+        $result['mail_send'] = $share->getMailSend() ? 1 : 0;
281
+        $result['hide_download'] = $share->getHideDownload() ? 1 : 0;
282
+
283
+        return $result;
284
+    }
285
+
286
+    /**
287
+     * Check if one of the users address books knows the exact property, if
288
+     * yes we return the full name.
289
+     *
290
+     * @param string $query
291
+     * @param string $property
292
+     * @return string
293
+     */
294
+    private function getDisplayNameFromAddressBook(string $query, string $property): string {
295
+        // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
296
+        $result = \OC::$server->getContactsManager()->search($query, [$property]);
297
+        foreach ($result as $r) {
298
+            foreach ($r[$property] as $value) {
299
+                if ($value === $query) {
300
+                    return $r['FN'];
301
+                }
302
+            }
303
+        }
304
+
305
+        return $query;
306
+    }
307
+
308
+    /**
309
+     * Get a specific share by id
310
+     *
311
+     * @NoAdminRequired
312
+     *
313
+     * @param string $id
314
+     * @return DataResponse
315
+     * @throws OCSNotFoundException
316
+     */
317
+    public function getShare(string $id): DataResponse {
318
+        try {
319
+            $share = $this->getShareById($id);
320
+        } catch (ShareNotFound $e) {
321
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
322
+        }
323
+
324
+        try {
325
+            if ($this->canAccessShare($share)) {
326
+                $share = $this->formatShare($share);
327
+                return new DataResponse([$share]);
328
+            }
329
+        } catch (NotFoundException $e) {
330
+            // Fall trough
331
+        }
332
+
333
+        throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
334
+    }
335
+
336
+    /**
337
+     * Delete a share
338
+     *
339
+     * @NoAdminRequired
340
+     *
341
+     * @param string $id
342
+     * @return DataResponse
343
+     * @throws OCSNotFoundException
344
+     */
345
+    public function deleteShare(string $id): DataResponse {
346
+        try {
347
+            $share = $this->getShareById($id);
348
+        } catch (ShareNotFound $e) {
349
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
350
+        }
351
+
352
+        try {
353
+            $this->lock($share->getNode());
354
+        } catch (LockedException $e) {
355
+            throw new OCSNotFoundException($this->l->t('Could not delete share'));
356
+        }
357
+
358
+        if (!$this->canAccessShare($share)) {
359
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
360
+        }
361
+
362
+        // if it's a group share or a room share
363
+        // we don't delete the share, but only the
364
+        // mount point. Allowing it to be restored
365
+        // from the deleted shares
366
+        if ($this->canDeleteShareFromSelf($share)) {
367
+            $this->shareManager->deleteFromSelf($share, $this->currentUser);
368
+        } else {
369
+            if (!$this->canDeleteShare($share)) {
370
+                throw new OCSForbiddenException($this->l->t('Could not delete share'));
371
+            }
372
+
373
+            $this->shareManager->deleteShare($share);
374
+        }
375
+
376
+        return new DataResponse();
377
+    }
378
+
379
+    /**
380
+     * @NoAdminRequired
381
+     *
382
+     * @param string $path
383
+     * @param int $permissions
384
+     * @param int $shareType
385
+     * @param string $shareWith
386
+     * @param string $publicUpload
387
+     * @param string $password
388
+     * @param string $sendPasswordByTalk
389
+     * @param string $expireDate
390
+     * @param string $label
391
+     *
392
+     * @return DataResponse
393
+     * @throws NotFoundException
394
+     * @throws OCSBadRequestException
395
+     * @throws OCSException
396
+     * @throws OCSForbiddenException
397
+     * @throws OCSNotFoundException
398
+     * @throws InvalidPathException
399
+     * @suppress PhanUndeclaredClassMethod
400
+     */
401
+    public function createShare(
402
+        string $path = null,
403
+        int $permissions = null,
404
+        int $shareType = -1,
405
+        string $shareWith = null,
406
+        string $publicUpload = 'false',
407
+        string $password = '',
408
+        string $sendPasswordByTalk = null,
409
+        string $expireDate = '',
410
+        string $label = ''
411
+    ): DataResponse {
412
+        $share = $this->shareManager->newShare();
413
+
414
+        if ($permissions === null) {
415
+            $permissions = $this->config->getAppValue('core', 'shareapi_default_permissions', Constants::PERMISSION_ALL);
416
+        }
417
+
418
+        // Verify path
419
+        if ($path === null) {
420
+            throw new OCSNotFoundException($this->l->t('Please specify a file or folder path'));
421
+        }
422
+
423
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
424
+        try {
425
+            $path = $userFolder->get($path);
426
+        } catch (NotFoundException $e) {
427
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
428
+        }
429
+
430
+        $share->setNode($path);
431
+
432
+        try {
433
+            $this->lock($share->getNode());
434
+        } catch (LockedException $e) {
435
+            throw new OCSNotFoundException($this->l->t('Could not create share'));
436
+        }
437
+
438
+        if ($permissions < 0 || $permissions > Constants::PERMISSION_ALL) {
439
+            throw new OCSNotFoundException($this->l->t('invalid permissions'));
440
+        }
441
+
442
+        // Shares always require read permissions
443
+        $permissions |= Constants::PERMISSION_READ;
444
+
445
+        if ($path instanceof \OCP\Files\File) {
446
+            // Single file shares should never have delete or create permissions
447
+            $permissions &= ~Constants::PERMISSION_DELETE;
448
+            $permissions &= ~Constants::PERMISSION_CREATE;
449
+        }
450
+
451
+        /**
452
+         * Hack for https://github.com/owncloud/core/issues/22587
453
+         * We check the permissions via webdav. But the permissions of the mount point
454
+         * do not equal the share permissions. Here we fix that for federated mounts.
455
+         */
456
+        if ($path->getStorage()->instanceOfStorage(Storage::class)) {
457
+            $permissions &= ~($permissions & ~$path->getPermissions());
458
+        }
459
+
460
+        if ($shareType === IShare::TYPE_USER) {
461
+            // Valid user is required to share
462
+            if ($shareWith === null || !$this->userManager->userExists($shareWith)) {
463
+                throw new OCSNotFoundException($this->l->t('Please specify a valid user'));
464
+            }
465
+            $share->setSharedWith($shareWith);
466
+            $share->setPermissions($permissions);
467
+        } elseif ($shareType === IShare::TYPE_GROUP) {
468
+            if (!$this->shareManager->allowGroupSharing()) {
469
+                throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
470
+            }
471
+
472
+            // Valid group is required to share
473
+            if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
474
+                throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
475
+            }
476
+            $share->setSharedWith($shareWith);
477
+            $share->setPermissions($permissions);
478
+        } elseif ($shareType === IShare::TYPE_LINK
479
+            || $shareType === IShare::TYPE_EMAIL) {
480
+
481
+            // Can we even share links?
482
+            if (!$this->shareManager->shareApiAllowLinks()) {
483
+                throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
484
+            }
485
+
486
+            if ($publicUpload === 'true') {
487
+                // Check if public upload is allowed
488
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
489
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
490
+                }
491
+
492
+                // Public upload can only be set for folders
493
+                if ($path instanceof \OCP\Files\File) {
494
+                    throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders'));
495
+                }
496
+
497
+                $permissions = Constants::PERMISSION_READ |
498
+                    Constants::PERMISSION_CREATE |
499
+                    Constants::PERMISSION_UPDATE |
500
+                    Constants::PERMISSION_DELETE;
501
+            } else {
502
+                $permissions = Constants::PERMISSION_READ;
503
+            }
504
+
505
+            // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
506
+            if (($permissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
507
+                $permissions |= Constants::PERMISSION_SHARE;
508
+            }
509
+
510
+            $share->setPermissions($permissions);
511
+
512
+            // Set password
513
+            if ($password !== '') {
514
+                $share->setPassword($password);
515
+            }
516
+
517
+            // Only share by mail have a recipient
518
+            if ($shareType === IShare::TYPE_EMAIL) {
519
+                $share->setSharedWith($shareWith);
520
+            } else {
521
+                // Only link share have a label
522
+                if (!empty($label)) {
523
+                    $share->setLabel($label);
524
+                }
525
+            }
526
+
527
+            if ($sendPasswordByTalk === 'true') {
528
+                if (!$this->appManager->isEnabledForUser('spreed')) {
529
+                    throw new OCSForbiddenException($this->l->t('Sharing %s sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled', [$path->getPath()]));
530
+                }
531
+
532
+                $share->setSendPasswordByTalk(true);
533
+            }
534
+
535
+            //Expire date
536
+            if ($expireDate !== '') {
537
+                try {
538
+                    $expireDate = $this->parseDate($expireDate);
539
+                    $share->setExpirationDate($expireDate);
540
+                } catch (\Exception $e) {
541
+                    throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD'));
542
+                }
543
+            }
544
+        } elseif ($shareType === IShare::TYPE_REMOTE) {
545
+            if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
546
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
547
+            }
548
+
549
+            $share->setSharedWith($shareWith);
550
+            $share->setPermissions($permissions);
551
+        } elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
552
+            if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
553
+                throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$path->getPath(), $shareType]));
554
+            }
555
+
556
+            $share->setSharedWith($shareWith);
557
+            $share->setPermissions($permissions);
558
+        } elseif ($shareType === IShare::TYPE_CIRCLE) {
559
+            if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
560
+                throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled'));
561
+            }
562
+
563
+            $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith);
564
+
565
+            // Valid circle is required to share
566
+            if ($circle === null) {
567
+                throw new OCSNotFoundException($this->l->t('Please specify a valid circle'));
568
+            }
569
+            $share->setSharedWith($shareWith);
570
+            $share->setPermissions($permissions);
571
+        } elseif ($shareType === IShare::TYPE_ROOM) {
572
+            try {
573
+                $this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate);
574
+            } catch (QueryException $e) {
575
+                throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$path->getPath()]));
576
+            }
577
+        } else {
578
+            throw new OCSBadRequestException($this->l->t('Unknown share type'));
579
+        }
580
+
581
+        $share->setShareType($shareType);
582
+        $share->setSharedBy($this->currentUser);
583
+
584
+        try {
585
+            $share = $this->shareManager->createShare($share);
586
+        } catch (GenericShareException $e) {
587
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
588
+            throw new OCSException($e->getHint(), $code);
589
+        } catch (\Exception $e) {
590
+            throw new OCSForbiddenException($e->getMessage(), $e);
591
+        }
592
+
593
+        $output = $this->formatShare($share);
594
+
595
+        return new DataResponse($output);
596
+    }
597
+
598
+    /**
599
+     * @param null|Node $node
600
+     * @param boolean $includeTags
601
+     *
602
+     * @return array
603
+     */
604
+    private function getSharedWithMe($node, bool $includeTags): array {
605
+        $userShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_USER, $node, -1, 0);
606
+        $groupShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_GROUP, $node, -1, 0);
607
+        $circleShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_CIRCLE, $node, -1, 0);
608
+        $roomShares = $this->shareManager->getSharedWith($this->currentUser, IShare::TYPE_ROOM, $node, -1, 0);
609
+
610
+        $shares = array_merge($userShares, $groupShares, $circleShares, $roomShares);
611
+
612
+        $filteredShares = array_filter($shares, function (IShare $share) {
613
+            return $share->getShareOwner() !== $this->currentUser;
614
+        });
615
+
616
+        $formatted = [];
617
+        foreach ($filteredShares as $share) {
618
+            if ($this->canAccessShare($share)) {
619
+                try {
620
+                    $formatted[] = $this->formatShare($share);
621
+                } catch (NotFoundException $e) {
622
+                    // Ignore this share
623
+                }
624
+            }
625
+        }
626
+
627
+        if ($includeTags) {
628
+            $formatted = Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
629
+        }
630
+
631
+        return $formatted;
632
+    }
633
+
634
+    /**
635
+     * @param \OCP\Files\Node $folder
636
+     *
637
+     * @return array
638
+     * @throws OCSBadRequestException
639
+     * @throws NotFoundException
640
+     */
641
+    private function getSharesInDir(Node $folder): array {
642
+        if (!($folder instanceof \OCP\Files\Folder)) {
643
+            throw new OCSBadRequestException($this->l->t('Not a directory'));
644
+        }
645
+
646
+        $nodes = $folder->getDirectoryListing();
647
+
648
+        /** @var \OCP\Share\IShare[] $shares */
649
+        $shares = array_reduce($nodes, function ($carry, $node) {
650
+            $carry = array_merge($carry, $this->getAllShares($node, true));
651
+            return $carry;
652
+        }, []);
653
+
654
+        // filter out duplicate shares
655
+        $known = [];
656
+
657
+
658
+        $formatted = $miniFormatted = [];
659
+        $resharingRight = false;
660
+        $known = [];
661
+        foreach ($shares as $share) {
662
+            if (in_array($share->getId(), $known) || $share->getSharedWith() === $this->currentUser) {
663
+                continue;
664
+            }
665
+
666
+            try {
667
+                $format = $this->formatShare($share);
668
+
669
+                $known[] = $share->getId();
670
+                $formatted[] = $format;
671
+                if ($share->getSharedBy() === $this->currentUser) {
672
+                    $miniFormatted[] = $format;
673
+                }
674
+                if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $folder)) {
675
+                    $resharingRight = true;
676
+                }
677
+            } catch (\Exception $e) {
678
+                //Ignore this share
679
+            }
680
+        }
681
+
682
+        if (!$resharingRight) {
683
+            $formatted = $miniFormatted;
684
+        }
685
+
686
+        return $formatted;
687
+    }
688
+
689
+    /**
690
+     * The getShares function.
691
+     *
692
+     * @NoAdminRequired
693
+     *
694
+     * @param string $shared_with_me
695
+     * @param string $reshares
696
+     * @param string $subfiles
697
+     * @param string $path
698
+     *
699
+     * - Get shares by the current user
700
+     * - Get shares by the current user and reshares (?reshares=true)
701
+     * - Get shares with the current user (?shared_with_me=true)
702
+     * - Get shares for a specific path (?path=...)
703
+     * - Get all shares in a folder (?subfiles=true&path=..)
704
+     *
705
+     * @param string $include_tags
706
+     *
707
+     * @return DataResponse
708
+     * @throws NotFoundException
709
+     * @throws OCSBadRequestException
710
+     * @throws OCSNotFoundException
711
+     */
712
+    public function getShares(
713
+        string $shared_with_me = 'false',
714
+        string $reshares = 'false',
715
+        string $subfiles = 'false',
716
+        string $path = '',
717
+        string $include_tags = 'false'
718
+    ): DataResponse {
719
+        $node = null;
720
+        if ($path !== '') {
721
+            $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
722
+            try {
723
+                $node = $userFolder->get($path);
724
+                $this->lock($node);
725
+            } catch (NotFoundException $e) {
726
+                throw new OCSNotFoundException(
727
+                    $this->l->t('Wrong path, file/folder doesn\'t exist')
728
+                );
729
+            } catch (LockedException $e) {
730
+                throw new OCSNotFoundException($this->l->t('Could not lock node'));
731
+            }
732
+        }
733
+
734
+        $shares = $this->getFormattedShares(
735
+            $this->currentUser,
736
+            $node,
737
+            ($shared_with_me === 'true'),
738
+            ($reshares === 'true'),
739
+            ($subfiles === 'true'),
740
+            ($include_tags === 'true')
741
+        );
742
+
743
+        return new DataResponse($shares);
744
+    }
745
+
746
+
747
+    /**
748
+     * @param string $viewer
749
+     * @param Node $node
750
+     * @param bool $sharedWithMe
751
+     * @param bool $reShares
752
+     * @param bool $subFiles
753
+     * @param bool $includeTags
754
+     *
755
+     * @return array
756
+     * @throws NotFoundException
757
+     * @throws OCSBadRequestException
758
+     */
759
+    private function getFormattedShares(
760
+        string $viewer,
761
+        $node = null,
762
+        bool $sharedWithMe = false,
763
+        bool $reShares = false,
764
+        bool $subFiles = false,
765
+        bool $includeTags = false
766
+    ): array {
767
+        if ($sharedWithMe) {
768
+            return $this->getSharedWithMe($node, $includeTags);
769
+        }
770
+
771
+        if ($subFiles) {
772
+            return $this->getSharesInDir($node);
773
+        }
774
+
775
+        $shares = $this->getSharesFromNode($viewer, $node, $reShares);
776
+
777
+        $known = $formatted = $miniFormatted = [];
778
+        $resharingRight = false;
779
+        foreach ($shares as $share) {
780
+            try {
781
+                $share->getNode();
782
+            } catch (NotFoundException $e) {
783
+                /*
784 784
 				 * Ignore shares where we can't get the node
785 785
 				 * For example deleted shares
786 786
 				 */
787
-				continue;
788
-			}
789
-
790
-			if (in_array($share->getId(), $known)
791
-				|| ($share->getSharedWith() === $this->currentUser && $share->getShareType() === IShare::TYPE_USER)) {
792
-				continue;
793
-			}
794
-
795
-			$known[] = $share->getId();
796
-			try {
797
-				/** @var IShare $share */
798
-				$format = $this->formatShare($share, $node);
799
-				$formatted[] = $format;
800
-
801
-				// let's also build a list of shares created
802
-				// by the current user only, in case
803
-				// there is no resharing rights
804
-				if ($share->getSharedBy() === $this->currentUser) {
805
-					$miniFormatted[] = $format;
806
-				}
807
-
808
-				// check if one of those share is shared with me
809
-				// and if I have resharing rights on it
810
-				if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $node)) {
811
-					$resharingRight = true;
812
-				}
813
-			} catch (InvalidPathException | NotFoundException $e) {
814
-			}
815
-		}
816
-
817
-		if (!$resharingRight) {
818
-			$formatted = $miniFormatted;
819
-		}
820
-
821
-		if ($includeTags) {
822
-			$formatted =
823
-				Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
824
-		}
825
-
826
-		return $formatted;
827
-	}
828
-
829
-
830
-	/**
831
-	 * The getInheritedShares function.
832
-	 * returns all shares relative to a file, including parent folders shares rights.
833
-	 *
834
-	 * @NoAdminRequired
835
-	 *
836
-	 * @param string $path
837
-	 *
838
-	 * - Get shares by the current user
839
-	 * - Get shares by the current user and reshares (?reshares=true)
840
-	 * - Get shares with the current user (?shared_with_me=true)
841
-	 * - Get shares for a specific path (?path=...)
842
-	 * - Get all shares in a folder (?subfiles=true&path=..)
843
-	 *
844
-	 * @return DataResponse
845
-	 * @throws InvalidPathException
846
-	 * @throws NotFoundException
847
-	 * @throws OCSNotFoundException
848
-	 * @throws OCSBadRequestException
849
-	 * @throws SharingRightsException
850
-	 */
851
-	public function getInheritedShares(string $path): DataResponse {
852
-
853
-		// get Node from (string) path.
854
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
855
-		try {
856
-			$node = $userFolder->get($path);
857
-			$this->lock($node);
858
-		} catch (\OCP\Files\NotFoundException $e) {
859
-			throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
860
-		} catch (LockedException $e) {
861
-			throw new OCSNotFoundException($this->l->t('Could not lock path'));
862
-		}
863
-
864
-		if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
865
-			throw new SharingRightsException('no sharing rights on this item');
866
-		}
867
-
868
-		// The current top parent we have access to
869
-		$parent = $node;
870
-
871
-		// initiate real owner.
872
-		$owner = $node->getOwner()
873
-					  ->getUID();
874
-		if (!$this->userManager->userExists($owner)) {
875
-			return new DataResponse([]);
876
-		}
877
-
878
-		// get node based on the owner, fix owner in case of external storage
879
-		$userFolder = $this->rootFolder->getUserFolder($owner);
880
-		if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
881
-			$owner = $node->getOwner()
882
-						  ->getUID();
883
-			$userFolder = $this->rootFolder->getUserFolder($owner);
884
-			$nodes = $userFolder->getById($node->getId());
885
-			$node = array_shift($nodes);
886
-		}
887
-		$basePath = $userFolder->getPath();
888
-
889
-		// generate node list for each parent folders
890
-		/** @var Node[] $nodes */
891
-		$nodes = [];
892
-		while ($node->getPath() !== $basePath) {
893
-			$node = $node->getParent();
894
-			$nodes[] = $node;
895
-		}
896
-
897
-		// The user that is requesting this list
898
-		$currentUserFolder = $this->rootFolder->getUserFolder($this->currentUser);
899
-
900
-		// for each nodes, retrieve shares.
901
-		$shares = [];
902
-
903
-		foreach ($nodes as $node) {
904
-			$getShares = $this->getFormattedShares($owner, $node, false, true);
905
-
906
-			$currentUserNodes = $currentUserFolder->getById($node->getId());
907
-			if (!empty($currentUserNodes)) {
908
-				$parent = array_pop($currentUserNodes);
909
-			}
910
-
911
-			$subPath = $currentUserFolder->getRelativePath($parent->getPath());
912
-			foreach ($getShares as &$share) {
913
-				$share['via_fileid'] = $parent->getId();
914
-				$share['via_path'] = $subPath;
915
-			}
916
-			$this->mergeFormattedShares($shares, $getShares);
917
-		}
918
-
919
-		return new DataResponse(array_values($shares));
920
-	}
921
-
922
-
923
-	/**
924
-	 * @NoAdminRequired
925
-	 *
926
-	 * @param string $id
927
-	 * @param int $permissions
928
-	 * @param string $password
929
-	 * @param string $sendPasswordByTalk
930
-	 * @param string $publicUpload
931
-	 * @param string $expireDate
932
-	 * @param string $note
933
-	 * @param string $label
934
-	 * @param string $hideDownload
935
-	 * @return DataResponse
936
-	 * @throws LockedException
937
-	 * @throws NotFoundException
938
-	 * @throws OCSBadRequestException
939
-	 * @throws OCSForbiddenException
940
-	 * @throws OCSNotFoundException
941
-	 */
942
-	public function updateShare(
943
-		string $id,
944
-		int $permissions = null,
945
-		string $password = null,
946
-		string $sendPasswordByTalk = null,
947
-		string $publicUpload = null,
948
-		string $expireDate = null,
949
-		string $note = null,
950
-		string $label = null,
951
-		string $hideDownload = null
952
-	): DataResponse {
953
-		try {
954
-			$share = $this->getShareById($id);
955
-		} catch (ShareNotFound $e) {
956
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
957
-		}
958
-
959
-		$this->lock($share->getNode());
960
-
961
-		if (!$this->canAccessShare($share, false)) {
962
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
963
-		}
964
-
965
-		if (!$this->canEditShare($share)) {
966
-			throw new OCSForbiddenException('You are not allowed to edit incoming shares');
967
-		}
968
-
969
-		if (
970
-			$permissions === null &&
971
-			$password === null &&
972
-			$sendPasswordByTalk === null &&
973
-			$publicUpload === null &&
974
-			$expireDate === null &&
975
-			$note === null &&
976
-			$label === null &&
977
-			$hideDownload === null
978
-		) {
979
-			throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
980
-		}
981
-
982
-		if ($note !== null) {
983
-			$share->setNote($note);
984
-		}
985
-
986
-		/**
987
-		 * expirationdate, password and publicUpload only make sense for link shares
988
-		 */
989
-		if ($share->getShareType() === IShare::TYPE_LINK
990
-			|| $share->getShareType() === IShare::TYPE_EMAIL) {
991
-
992
-			/**
993
-			 * We do not allow editing link shares that the current user
994
-			 * doesn't own. This is confusing and lead to errors when
995
-			 * someone else edit a password or expiration date without
996
-			 * the share owner knowing about it.
997
-			 * We only allow deletion
998
-			 */
999
-
1000
-			if ($share->getSharedBy() !== $this->currentUser) {
1001
-				throw new OCSForbiddenException('You are not allowed to edit link shares that you don\'t own');
1002
-			}
1003
-
1004
-			// Update hide download state
1005
-			if ($hideDownload === 'true') {
1006
-				$share->setHideDownload(true);
1007
-			} elseif ($hideDownload === 'false') {
1008
-				$share->setHideDownload(false);
1009
-			}
1010
-
1011
-			$newPermissions = null;
1012
-			if ($publicUpload === 'true') {
1013
-				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1014
-			} elseif ($publicUpload === 'false') {
1015
-				$newPermissions = Constants::PERMISSION_READ;
1016
-			}
1017
-
1018
-			if ($permissions !== null) {
1019
-				$newPermissions = (int) $permissions;
1020
-				$newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
1021
-			}
1022
-
1023
-			if ($newPermissions !== null &&
1024
-				!in_array($newPermissions, [
1025
-					Constants::PERMISSION_READ,
1026
-					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE, // legacy
1027
-					Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE, // correct
1028
-					Constants::PERMISSION_CREATE, // hidden file list
1029
-					Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, // allow to edit single files
1030
-				], true)
1031
-			) {
1032
-				throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
1033
-			}
1034
-
1035
-			if (
1036
-				// legacy
1037
-				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
1038
-				// correct
1039
-				$newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1040
-			) {
1041
-				if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
1042
-					throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1043
-				}
1044
-
1045
-				if (!($share->getNode() instanceof \OCP\Files\Folder)) {
1046
-					throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1047
-				}
1048
-
1049
-				// normalize to correct public upload permissions
1050
-				$newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1051
-			}
1052
-
1053
-			if ($newPermissions !== null) {
1054
-				// TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
1055
-				if (($newPermissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
1056
-					$newPermissions |= Constants::PERMISSION_SHARE;
1057
-				}
1058
-
1059
-				$share->setPermissions($newPermissions);
1060
-				$permissions = $newPermissions;
1061
-			}
1062
-
1063
-			if ($expireDate === '') {
1064
-				$share->setExpirationDate(null);
1065
-			} elseif ($expireDate !== null) {
1066
-				try {
1067
-					$expireDate = $this->parseDate($expireDate);
1068
-				} catch (\Exception $e) {
1069
-					throw new OCSBadRequestException($e->getMessage(), $e);
1070
-				}
1071
-				$share->setExpirationDate($expireDate);
1072
-			}
1073
-
1074
-			if ($password === '') {
1075
-				$share->setPassword(null);
1076
-			} elseif ($password !== null) {
1077
-				$share->setPassword($password);
1078
-			}
1079
-
1080
-			// only link shares have labels
1081
-			if ($share->getShareType() === IShare::TYPE_LINK && $label !== null) {
1082
-				if (strlen($label) > 255) {
1083
-					throw new OCSBadRequestException("Maxmimum label length is 255");
1084
-				}
1085
-				$share->setLabel($label);
1086
-			}
1087
-
1088
-			if ($sendPasswordByTalk === 'true') {
1089
-				if (!$this->appManager->isEnabledForUser('spreed')) {
1090
-					throw new OCSForbiddenException($this->l->t('Sharing sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled'));
1091
-				}
1092
-
1093
-				$share->setSendPasswordByTalk(true);
1094
-			} elseif ($sendPasswordByTalk !== null) {
1095
-				$share->setSendPasswordByTalk(false);
1096
-			}
1097
-		}
1098
-
1099
-		// NOT A LINK SHARE
1100
-		else {
1101
-			if ($permissions !== null) {
1102
-				$permissions = (int) $permissions;
1103
-				$share->setPermissions($permissions);
1104
-			}
1105
-
1106
-			if ($expireDate === '') {
1107
-				$share->setExpirationDate(null);
1108
-			} elseif ($expireDate !== null) {
1109
-				try {
1110
-					$expireDate = $this->parseDate($expireDate);
1111
-				} catch (\Exception $e) {
1112
-					throw new OCSBadRequestException($e->getMessage(), $e);
1113
-				}
1114
-				$share->setExpirationDate($expireDate);
1115
-			}
1116
-		}
1117
-
1118
-		try {
1119
-			$share = $this->shareManager->updateShare($share);
1120
-		} catch (GenericShareException $e) {
1121
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1122
-			throw new OCSException($e->getHint(), $code);
1123
-		} catch (\Exception $e) {
1124
-			throw new OCSBadRequestException($e->getMessage(), $e);
1125
-		}
1126
-
1127
-		return new DataResponse($this->formatShare($share));
1128
-	}
1129
-
1130
-	/**
1131
-	 * @NoAdminRequired
1132
-	 */
1133
-	public function pendingShares(): DataResponse {
1134
-		$pendingShares = [];
1135
-
1136
-		$shareTypes = [
1137
-			IShare::TYPE_USER,
1138
-			IShare::TYPE_GROUP
1139
-		];
1140
-
1141
-		foreach ($shareTypes as $shareType) {
1142
-			$shares = $this->shareManager->getSharedWith($this->currentUser, $shareType, null, -1, 0);
1143
-
1144
-			foreach ($shares as $share) {
1145
-				if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1146
-					$pendingShares[] = $share;
1147
-				}
1148
-			}
1149
-		}
1150
-
1151
-		$result = array_filter(array_map(function (IShare $share) {
1152
-			$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1153
-			$nodes = $userFolder->getById($share->getNodeId());
1154
-			if (empty($nodes)) {
1155
-				// fallback to guessing the path
1156
-				$node = $userFolder->get($share->getTarget());
1157
-				if ($node === null || $share->getTarget() === '') {
1158
-					return null;
1159
-				}
1160
-			} else {
1161
-				$node = $nodes[0];
1162
-			}
1163
-
1164
-			try {
1165
-				$formattedShare = $this->formatShare($share, $node);
1166
-				$formattedShare['status'] = $share->getStatus();
1167
-				$formattedShare['path'] = $share->getNode()->getName();
1168
-				$formattedShare['permissions'] = 0;
1169
-				return $formattedShare;
1170
-			} catch (NotFoundException $e) {
1171
-				return null;
1172
-			}
1173
-		}, $pendingShares), function ($entry) {
1174
-			return $entry !== null;
1175
-		});
1176
-
1177
-		return new DataResponse($result);
1178
-	}
1179
-
1180
-	/**
1181
-	 * @NoAdminRequired
1182
-	 *
1183
-	 * @param string $id
1184
-	 * @return DataResponse
1185
-	 * @throws OCSNotFoundException
1186
-	 * @throws OCSException
1187
-	 * @throws OCSBadRequestException
1188
-	 */
1189
-	public function acceptShare(string $id): DataResponse {
1190
-		try {
1191
-			$share = $this->getShareById($id);
1192
-		} catch (ShareNotFound $e) {
1193
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1194
-		}
1195
-
1196
-		if (!$this->canAccessShare($share)) {
1197
-			throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1198
-		}
1199
-
1200
-		try {
1201
-			$this->shareManager->acceptShare($share, $this->currentUser);
1202
-		} catch (GenericShareException $e) {
1203
-			$code = $e->getCode() === 0 ? 403 : $e->getCode();
1204
-			throw new OCSException($e->getHint(), $code);
1205
-		} catch (\Exception $e) {
1206
-			throw new OCSBadRequestException($e->getMessage(), $e);
1207
-		}
1208
-
1209
-		return new DataResponse();
1210
-	}
1211
-
1212
-	/**
1213
-	 * Does the user have read permission on the share
1214
-	 *
1215
-	 * @param \OCP\Share\IShare $share the share to check
1216
-	 * @param boolean $checkGroups check groups as well?
1217
-	 * @return boolean
1218
-	 * @throws NotFoundException
1219
-	 *
1220
-	 * @suppress PhanUndeclaredClassMethod
1221
-	 */
1222
-	protected function canAccessShare(\OCP\Share\IShare $share, bool $checkGroups = true): bool {
1223
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1224
-		if ($share->getPermissions() === 0) {
1225
-			return false;
1226
-		}
1227
-
1228
-		// Owner of the file and the sharer of the file can always get share
1229
-		if ($share->getShareOwner() === $this->currentUser
1230
-			|| $share->getSharedBy() === $this->currentUser) {
1231
-			return true;
1232
-		}
1233
-
1234
-		// If the share is shared with you, you can access it!
1235
-		if ($share->getShareType() === IShare::TYPE_USER
1236
-			&& $share->getSharedWith() === $this->currentUser) {
1237
-			return true;
1238
-		}
1239
-
1240
-		// Have reshare rights on the shared file/folder ?
1241
-		// Does the currentUser have access to the shared file?
1242
-		$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
1243
-		$files = $userFolder->getById($share->getNodeId());
1244
-		if (!empty($files) && $this->shareProviderResharingRights($this->currentUser, $share, $files[0])) {
1245
-			return true;
1246
-		}
1247
-
1248
-		// If in the recipient group, you can see the share
1249
-		if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1250
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1251
-			$user = $this->userManager->get($this->currentUser);
1252
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1253
-				return true;
1254
-			}
1255
-		}
1256
-
1257
-		if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1258
-			// TODO: have a sanity check like above?
1259
-			return true;
1260
-		}
1261
-
1262
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1263
-			try {
1264
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1265
-			} catch (QueryException $e) {
1266
-				return false;
1267
-			}
1268
-		}
1269
-
1270
-		return false;
1271
-	}
1272
-
1273
-	/**
1274
-	 * Does the user have edit permission on the share
1275
-	 *
1276
-	 * @param \OCP\Share\IShare $share the share to check
1277
-	 * @return boolean
1278
-	 */
1279
-	protected function canEditShare(\OCP\Share\IShare $share): bool {
1280
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1281
-		if ($share->getPermissions() === 0) {
1282
-			return false;
1283
-		}
1284
-
1285
-		// The owner of the file and the creator of the share
1286
-		// can always edit the share
1287
-		if ($share->getShareOwner() === $this->currentUser ||
1288
-			$share->getSharedBy() === $this->currentUser
1289
-		) {
1290
-			return true;
1291
-		}
1292
-
1293
-		//! we do NOT support some kind of `admin` in groups.
1294
-		//! You cannot edit shares shared to a group you're
1295
-		//! a member of if you're not the share owner or the file owner!
1296
-
1297
-		return false;
1298
-	}
1299
-
1300
-	/**
1301
-	 * Does the user have delete permission on the share
1302
-	 *
1303
-	 * @param \OCP\Share\IShare $share the share to check
1304
-	 * @return boolean
1305
-	 */
1306
-	protected function canDeleteShare(\OCP\Share\IShare $share): bool {
1307
-		// A file with permissions 0 can't be accessed by us. So Don't show it
1308
-		if ($share->getPermissions() === 0) {
1309
-			return false;
1310
-		}
1311
-
1312
-		// if the user is the recipient, i can unshare
1313
-		// the share with self
1314
-		if ($share->getShareType() === IShare::TYPE_USER &&
1315
-			$share->getSharedWith() === $this->currentUser
1316
-		) {
1317
-			return true;
1318
-		}
1319
-
1320
-		// The owner of the file and the creator of the share
1321
-		// can always delete the share
1322
-		if ($share->getShareOwner() === $this->currentUser ||
1323
-			$share->getSharedBy() === $this->currentUser
1324
-		) {
1325
-			return true;
1326
-		}
1327
-
1328
-		return false;
1329
-	}
1330
-
1331
-	/**
1332
-	 * Does the user have delete permission on the share
1333
-	 * This differs from the canDeleteShare function as it only
1334
-	 * remove the share for the current user. It does NOT
1335
-	 * completely delete the share but only the mount point.
1336
-	 * It can then be restored from the deleted shares section.
1337
-	 *
1338
-	 * @param \OCP\Share\IShare $share the share to check
1339
-	 * @return boolean
1340
-	 *
1341
-	 * @suppress PhanUndeclaredClassMethod
1342
-	 */
1343
-	protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
1344
-		if ($share->getShareType() !== IShare::TYPE_GROUP &&
1345
-			$share->getShareType() !== IShare::TYPE_ROOM
1346
-		) {
1347
-			return false;
1348
-		}
1349
-
1350
-		if ($share->getShareOwner() === $this->currentUser ||
1351
-			$share->getSharedBy() === $this->currentUser
1352
-		) {
1353
-			// Delete the whole share, not just for self
1354
-			return false;
1355
-		}
1356
-
1357
-		// If in the recipient group, you can delete the share from self
1358
-		if ($share->getShareType() === IShare::TYPE_GROUP) {
1359
-			$sharedWith = $this->groupManager->get($share->getSharedWith());
1360
-			$user = $this->userManager->get($this->currentUser);
1361
-			if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1362
-				return true;
1363
-			}
1364
-		}
1365
-
1366
-		if ($share->getShareType() === IShare::TYPE_ROOM) {
1367
-			try {
1368
-				return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1369
-			} catch (QueryException $e) {
1370
-				return false;
1371
-			}
1372
-		}
1373
-
1374
-		return false;
1375
-	}
1376
-
1377
-	/**
1378
-	 * Make sure that the passed date is valid ISO 8601
1379
-	 * So YYYY-MM-DD
1380
-	 * If not throw an exception
1381
-	 *
1382
-	 * @param string $expireDate
1383
-	 *
1384
-	 * @throws \Exception
1385
-	 * @return \DateTime
1386
-	 */
1387
-	private function parseDate(string $expireDate): \DateTime {
1388
-		try {
1389
-			$date = new \DateTime($expireDate);
1390
-		} catch (\Exception $e) {
1391
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1392
-		}
1393
-
1394
-		if ($date === false) {
1395
-			throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1396
-		}
1397
-
1398
-		$date->setTime(0, 0, 0);
1399
-
1400
-		return $date;
1401
-	}
1402
-
1403
-	/**
1404
-	 * Since we have multiple providers but the OCS Share API v1 does
1405
-	 * not support this we need to check all backends.
1406
-	 *
1407
-	 * @param string $id
1408
-	 * @return \OCP\Share\IShare
1409
-	 * @throws ShareNotFound
1410
-	 */
1411
-	private function getShareById(string $id): IShare {
1412
-		$share = null;
1413
-
1414
-		// First check if it is an internal share.
1415
-		try {
1416
-			$share = $this->shareManager->getShareById('ocinternal:' . $id, $this->currentUser);
1417
-			return $share;
1418
-		} catch (ShareNotFound $e) {
1419
-			// Do nothing, just try the other share type
1420
-		}
1421
-
1422
-
1423
-		try {
1424
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_CIRCLE)) {
1425
-				$share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->currentUser);
1426
-				return $share;
1427
-			}
1428
-		} catch (ShareNotFound $e) {
1429
-			// Do nothing, just try the other share type
1430
-		}
1431
-
1432
-		try {
1433
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
1434
-				$share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->currentUser);
1435
-				return $share;
1436
-			}
1437
-		} catch (ShareNotFound $e) {
1438
-			// Do nothing, just try the other share type
1439
-		}
1440
-
1441
-		try {
1442
-			$share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->currentUser);
1443
-			return $share;
1444
-		} catch (ShareNotFound $e) {
1445
-			// Do nothing, just try the other share type
1446
-		}
1447
-
1448
-		if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1449
-			throw new ShareNotFound();
1450
-		}
1451
-		$share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->currentUser);
1452
-
1453
-		return $share;
1454
-	}
1455
-
1456
-	/**
1457
-	 * Lock a Node
1458
-	 *
1459
-	 * @param \OCP\Files\Node $node
1460
-	 * @throws LockedException
1461
-	 */
1462
-	private function lock(\OCP\Files\Node $node) {
1463
-		$node->lock(ILockingProvider::LOCK_SHARED);
1464
-		$this->lockedNode = $node;
1465
-	}
1466
-
1467
-	/**
1468
-	 * Cleanup the remaining locks
1469
-	 * @throws LockedException
1470
-	 */
1471
-	public function cleanup() {
1472
-		if ($this->lockedNode !== null) {
1473
-			$this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1474
-		}
1475
-	}
1476
-
1477
-	/**
1478
-	 * Returns the helper of ShareAPIController for room shares.
1479
-	 *
1480
-	 * If the Talk application is not enabled or the helper is not available
1481
-	 * a QueryException is thrown instead.
1482
-	 *
1483
-	 * @return \OCA\Talk\Share\Helper\ShareAPIController
1484
-	 * @throws QueryException
1485
-	 */
1486
-	private function getRoomShareHelper() {
1487
-		if (!$this->appManager->isEnabledForUser('spreed')) {
1488
-			throw new QueryException();
1489
-		}
1490
-
1491
-		return $this->serverContainer->query('\OCA\Talk\Share\Helper\ShareAPIController');
1492
-	}
1493
-
1494
-
1495
-	/**
1496
-	 * @param string $viewer
1497
-	 * @param Node $node
1498
-	 * @param bool $reShares
1499
-	 *
1500
-	 * @return IShare[]
1501
-	 */
1502
-	private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1503
-		$providers = [
1504
-			IShare::TYPE_USER,
1505
-			IShare::TYPE_GROUP,
1506
-			IShare::TYPE_LINK,
1507
-			IShare::TYPE_EMAIL,
1508
-			IShare::TYPE_EMAIL,
1509
-			IShare::TYPE_CIRCLE,
1510
-			IShare::TYPE_ROOM
1511
-		];
1512
-
1513
-		// Should we assume that the (currentUser) viewer is the owner of the node !?
1514
-		$shares = [];
1515
-		foreach ($providers as $provider) {
1516
-			if (!$this->shareManager->shareProviderExists($provider)) {
1517
-				continue;
1518
-			}
1519
-
1520
-			$providerShares =
1521
-				$this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1522
-			$shares = array_merge($shares, $providerShares);
1523
-		}
1524
-
1525
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1526
-			$federatedShares = $this->shareManager->getSharesBy(
1527
-				$this->currentUser, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1528
-			);
1529
-			$shares = array_merge($shares, $federatedShares);
1530
-		}
1531
-
1532
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1533
-			$federatedShares = $this->shareManager->getSharesBy(
1534
-				$this->currentUser, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1535
-			);
1536
-			$shares = array_merge($shares, $federatedShares);
1537
-		}
1538
-
1539
-		return $shares;
1540
-	}
1541
-
1542
-
1543
-	/**
1544
-	 * @param Node $node
1545
-	 *
1546
-	 * @throws SharingRightsException
1547
-	 */
1548
-	private function confirmSharingRights(Node $node): void {
1549
-		if (!$this->hasResharingRights($this->currentUser, $node)) {
1550
-			throw new SharingRightsException('no sharing rights on this item');
1551
-		}
1552
-	}
1553
-
1554
-
1555
-	/**
1556
-	 * @param string $viewer
1557
-	 * @param Node $node
1558
-	 *
1559
-	 * @return bool
1560
-	 */
1561
-	private function hasResharingRights($viewer, $node): bool {
1562
-		if ($viewer === $node->getOwner()->getUID()) {
1563
-			return true;
1564
-		}
1565
-
1566
-		foreach ([$node, $node->getParent()] as $node) {
1567
-			$shares = $this->getSharesFromNode($viewer, $node, true);
1568
-			foreach ($shares as $share) {
1569
-				try {
1570
-					if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1571
-						return true;
1572
-					}
1573
-				} catch (InvalidPathException | NotFoundException $e) {
1574
-				}
1575
-			}
1576
-		}
1577
-
1578
-		return false;
1579
-	}
1580
-
1581
-
1582
-	/**
1583
-	 * Returns if we can find resharing rights in an IShare object for a specific user.
1584
-	 *
1585
-	 * @suppress PhanUndeclaredClassMethod
1586
-	 *
1587
-	 * @param string $userId
1588
-	 * @param IShare $share
1589
-	 * @param Node $node
1590
-	 *
1591
-	 * @return bool
1592
-	 * @throws NotFoundException
1593
-	 * @throws InvalidPathException
1594
-	 */
1595
-	private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1596
-		if ($share->getShareOwner() === $userId) {
1597
-			return true;
1598
-		}
1599
-
1600
-		// we check that current user have parent resharing rights on the current file
1601
-		if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1602
-			return true;
1603
-		}
1604
-
1605
-		if ((\OCP\Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1606
-			return false;
1607
-		}
1608
-
1609
-		if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1610
-			return true;
1611
-		}
1612
-
1613
-		if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1614
-			return true;
1615
-		}
1616
-
1617
-		if ($share->getShareType() === IShare::TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
1618
-			&& class_exists('\OCA\Circles\Api\v1\Circles')) {
1619
-			$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
1620
-			$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1621
-			$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1622
-			if (is_bool($shareWithLength)) {
1623
-				$shareWithLength = -1;
1624
-			}
1625
-			$sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1626
-			try {
1627
-				$member = \OCA\Circles\Api\v1\Circles::getMember($sharedWith, $userId, 1);
1628
-				if ($member->getLevel() >= 4) {
1629
-					return true;
1630
-				}
1631
-				return false;
1632
-			} catch (QueryException $e) {
1633
-				return false;
1634
-			}
1635
-		}
1636
-
1637
-		return false;
1638
-	}
1639
-
1640
-	/**
1641
-	 * Get all the shares for the current user
1642
-	 *
1643
-	 * @param Node|null $path
1644
-	 * @param boolean $reshares
1645
-	 * @return IShare[]
1646
-	 */
1647
-	private function getAllShares(?Node $path = null, bool $reshares = false) {
1648
-		// Get all shares
1649
-		$userShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_USER, $path, $reshares, -1, 0);
1650
-		$groupShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
1651
-		$linkShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_LINK, $path, $reshares, -1, 0);
1652
-
1653
-		// EMAIL SHARES
1654
-		$mailShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
1655
-
1656
-		// CIRCLE SHARES
1657
-		$circleShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
1658
-
1659
-		// TALK SHARES
1660
-		$roomShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
1661
-
1662
-		// FEDERATION
1663
-		if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1664
-			$federatedShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
1665
-		} else {
1666
-			$federatedShares = [];
1667
-		}
1668
-		if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1669
-			$federatedGroupShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
1670
-		} else {
1671
-			$federatedGroupShares = [];
1672
-		}
1673
-
1674
-		return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $federatedShares, $federatedGroupShares);
1675
-	}
1676
-
1677
-
1678
-	/**
1679
-	 * merging already formatted shares.
1680
-	 * We'll make an associative array to easily detect duplicate Ids.
1681
-	 * Keys _needs_ to be removed after all shares are retrieved and merged.
1682
-	 *
1683
-	 * @param array $shares
1684
-	 * @param array $newShares
1685
-	 */
1686
-	private function mergeFormattedShares(array &$shares, array $newShares) {
1687
-		foreach ($newShares as $newShare) {
1688
-			if (!array_key_exists($newShare['id'], $shares)) {
1689
-				$shares[$newShare['id']] = $newShare;
1690
-			}
1691
-		}
1692
-	}
787
+                continue;
788
+            }
789
+
790
+            if (in_array($share->getId(), $known)
791
+                || ($share->getSharedWith() === $this->currentUser && $share->getShareType() === IShare::TYPE_USER)) {
792
+                continue;
793
+            }
794
+
795
+            $known[] = $share->getId();
796
+            try {
797
+                /** @var IShare $share */
798
+                $format = $this->formatShare($share, $node);
799
+                $formatted[] = $format;
800
+
801
+                // let's also build a list of shares created
802
+                // by the current user only, in case
803
+                // there is no resharing rights
804
+                if ($share->getSharedBy() === $this->currentUser) {
805
+                    $miniFormatted[] = $format;
806
+                }
807
+
808
+                // check if one of those share is shared with me
809
+                // and if I have resharing rights on it
810
+                if (!$resharingRight && $this->shareProviderResharingRights($this->currentUser, $share, $node)) {
811
+                    $resharingRight = true;
812
+                }
813
+            } catch (InvalidPathException | NotFoundException $e) {
814
+            }
815
+        }
816
+
817
+        if (!$resharingRight) {
818
+            $formatted = $miniFormatted;
819
+        }
820
+
821
+        if ($includeTags) {
822
+            $formatted =
823
+                Helper::populateTags($formatted, 'file_source', \OC::$server->getTagManager());
824
+        }
825
+
826
+        return $formatted;
827
+    }
828
+
829
+
830
+    /**
831
+     * The getInheritedShares function.
832
+     * returns all shares relative to a file, including parent folders shares rights.
833
+     *
834
+     * @NoAdminRequired
835
+     *
836
+     * @param string $path
837
+     *
838
+     * - Get shares by the current user
839
+     * - Get shares by the current user and reshares (?reshares=true)
840
+     * - Get shares with the current user (?shared_with_me=true)
841
+     * - Get shares for a specific path (?path=...)
842
+     * - Get all shares in a folder (?subfiles=true&path=..)
843
+     *
844
+     * @return DataResponse
845
+     * @throws InvalidPathException
846
+     * @throws NotFoundException
847
+     * @throws OCSNotFoundException
848
+     * @throws OCSBadRequestException
849
+     * @throws SharingRightsException
850
+     */
851
+    public function getInheritedShares(string $path): DataResponse {
852
+
853
+        // get Node from (string) path.
854
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
855
+        try {
856
+            $node = $userFolder->get($path);
857
+            $this->lock($node);
858
+        } catch (\OCP\Files\NotFoundException $e) {
859
+            throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist'));
860
+        } catch (LockedException $e) {
861
+            throw new OCSNotFoundException($this->l->t('Could not lock path'));
862
+        }
863
+
864
+        if (!($node->getPermissions() & Constants::PERMISSION_SHARE)) {
865
+            throw new SharingRightsException('no sharing rights on this item');
866
+        }
867
+
868
+        // The current top parent we have access to
869
+        $parent = $node;
870
+
871
+        // initiate real owner.
872
+        $owner = $node->getOwner()
873
+                        ->getUID();
874
+        if (!$this->userManager->userExists($owner)) {
875
+            return new DataResponse([]);
876
+        }
877
+
878
+        // get node based on the owner, fix owner in case of external storage
879
+        $userFolder = $this->rootFolder->getUserFolder($owner);
880
+        if ($node->getId() !== $userFolder->getId() && !$userFolder->isSubNode($node)) {
881
+            $owner = $node->getOwner()
882
+                            ->getUID();
883
+            $userFolder = $this->rootFolder->getUserFolder($owner);
884
+            $nodes = $userFolder->getById($node->getId());
885
+            $node = array_shift($nodes);
886
+        }
887
+        $basePath = $userFolder->getPath();
888
+
889
+        // generate node list for each parent folders
890
+        /** @var Node[] $nodes */
891
+        $nodes = [];
892
+        while ($node->getPath() !== $basePath) {
893
+            $node = $node->getParent();
894
+            $nodes[] = $node;
895
+        }
896
+
897
+        // The user that is requesting this list
898
+        $currentUserFolder = $this->rootFolder->getUserFolder($this->currentUser);
899
+
900
+        // for each nodes, retrieve shares.
901
+        $shares = [];
902
+
903
+        foreach ($nodes as $node) {
904
+            $getShares = $this->getFormattedShares($owner, $node, false, true);
905
+
906
+            $currentUserNodes = $currentUserFolder->getById($node->getId());
907
+            if (!empty($currentUserNodes)) {
908
+                $parent = array_pop($currentUserNodes);
909
+            }
910
+
911
+            $subPath = $currentUserFolder->getRelativePath($parent->getPath());
912
+            foreach ($getShares as &$share) {
913
+                $share['via_fileid'] = $parent->getId();
914
+                $share['via_path'] = $subPath;
915
+            }
916
+            $this->mergeFormattedShares($shares, $getShares);
917
+        }
918
+
919
+        return new DataResponse(array_values($shares));
920
+    }
921
+
922
+
923
+    /**
924
+     * @NoAdminRequired
925
+     *
926
+     * @param string $id
927
+     * @param int $permissions
928
+     * @param string $password
929
+     * @param string $sendPasswordByTalk
930
+     * @param string $publicUpload
931
+     * @param string $expireDate
932
+     * @param string $note
933
+     * @param string $label
934
+     * @param string $hideDownload
935
+     * @return DataResponse
936
+     * @throws LockedException
937
+     * @throws NotFoundException
938
+     * @throws OCSBadRequestException
939
+     * @throws OCSForbiddenException
940
+     * @throws OCSNotFoundException
941
+     */
942
+    public function updateShare(
943
+        string $id,
944
+        int $permissions = null,
945
+        string $password = null,
946
+        string $sendPasswordByTalk = null,
947
+        string $publicUpload = null,
948
+        string $expireDate = null,
949
+        string $note = null,
950
+        string $label = null,
951
+        string $hideDownload = null
952
+    ): DataResponse {
953
+        try {
954
+            $share = $this->getShareById($id);
955
+        } catch (ShareNotFound $e) {
956
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
957
+        }
958
+
959
+        $this->lock($share->getNode());
960
+
961
+        if (!$this->canAccessShare($share, false)) {
962
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
963
+        }
964
+
965
+        if (!$this->canEditShare($share)) {
966
+            throw new OCSForbiddenException('You are not allowed to edit incoming shares');
967
+        }
968
+
969
+        if (
970
+            $permissions === null &&
971
+            $password === null &&
972
+            $sendPasswordByTalk === null &&
973
+            $publicUpload === null &&
974
+            $expireDate === null &&
975
+            $note === null &&
976
+            $label === null &&
977
+            $hideDownload === null
978
+        ) {
979
+            throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
980
+        }
981
+
982
+        if ($note !== null) {
983
+            $share->setNote($note);
984
+        }
985
+
986
+        /**
987
+         * expirationdate, password and publicUpload only make sense for link shares
988
+         */
989
+        if ($share->getShareType() === IShare::TYPE_LINK
990
+            || $share->getShareType() === IShare::TYPE_EMAIL) {
991
+
992
+            /**
993
+             * We do not allow editing link shares that the current user
994
+             * doesn't own. This is confusing and lead to errors when
995
+             * someone else edit a password or expiration date without
996
+             * the share owner knowing about it.
997
+             * We only allow deletion
998
+             */
999
+
1000
+            if ($share->getSharedBy() !== $this->currentUser) {
1001
+                throw new OCSForbiddenException('You are not allowed to edit link shares that you don\'t own');
1002
+            }
1003
+
1004
+            // Update hide download state
1005
+            if ($hideDownload === 'true') {
1006
+                $share->setHideDownload(true);
1007
+            } elseif ($hideDownload === 'false') {
1008
+                $share->setHideDownload(false);
1009
+            }
1010
+
1011
+            $newPermissions = null;
1012
+            if ($publicUpload === 'true') {
1013
+                $newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1014
+            } elseif ($publicUpload === 'false') {
1015
+                $newPermissions = Constants::PERMISSION_READ;
1016
+            }
1017
+
1018
+            if ($permissions !== null) {
1019
+                $newPermissions = (int) $permissions;
1020
+                $newPermissions = $newPermissions & ~Constants::PERMISSION_SHARE;
1021
+            }
1022
+
1023
+            if ($newPermissions !== null &&
1024
+                !in_array($newPermissions, [
1025
+                    Constants::PERMISSION_READ,
1026
+                    Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE, // legacy
1027
+                    Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE, // correct
1028
+                    Constants::PERMISSION_CREATE, // hidden file list
1029
+                    Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, // allow to edit single files
1030
+                ], true)
1031
+            ) {
1032
+                throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links'));
1033
+            }
1034
+
1035
+            if (
1036
+                // legacy
1037
+                $newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE) ||
1038
+                // correct
1039
+                $newPermissions === (Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE)
1040
+            ) {
1041
+                if (!$this->shareManager->shareApiLinkAllowPublicUpload()) {
1042
+                    throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1043
+                }
1044
+
1045
+                if (!($share->getNode() instanceof \OCP\Files\Folder)) {
1046
+                    throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders'));
1047
+                }
1048
+
1049
+                // normalize to correct public upload permissions
1050
+                $newPermissions = Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE;
1051
+            }
1052
+
1053
+            if ($newPermissions !== null) {
1054
+                // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
1055
+                if (($newPermissions & Constants::PERMISSION_READ) && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
1056
+                    $newPermissions |= Constants::PERMISSION_SHARE;
1057
+                }
1058
+
1059
+                $share->setPermissions($newPermissions);
1060
+                $permissions = $newPermissions;
1061
+            }
1062
+
1063
+            if ($expireDate === '') {
1064
+                $share->setExpirationDate(null);
1065
+            } elseif ($expireDate !== null) {
1066
+                try {
1067
+                    $expireDate = $this->parseDate($expireDate);
1068
+                } catch (\Exception $e) {
1069
+                    throw new OCSBadRequestException($e->getMessage(), $e);
1070
+                }
1071
+                $share->setExpirationDate($expireDate);
1072
+            }
1073
+
1074
+            if ($password === '') {
1075
+                $share->setPassword(null);
1076
+            } elseif ($password !== null) {
1077
+                $share->setPassword($password);
1078
+            }
1079
+
1080
+            // only link shares have labels
1081
+            if ($share->getShareType() === IShare::TYPE_LINK && $label !== null) {
1082
+                if (strlen($label) > 255) {
1083
+                    throw new OCSBadRequestException("Maxmimum label length is 255");
1084
+                }
1085
+                $share->setLabel($label);
1086
+            }
1087
+
1088
+            if ($sendPasswordByTalk === 'true') {
1089
+                if (!$this->appManager->isEnabledForUser('spreed')) {
1090
+                    throw new OCSForbiddenException($this->l->t('Sharing sending the password by Nextcloud Talk failed because Nextcloud Talk is not enabled'));
1091
+                }
1092
+
1093
+                $share->setSendPasswordByTalk(true);
1094
+            } elseif ($sendPasswordByTalk !== null) {
1095
+                $share->setSendPasswordByTalk(false);
1096
+            }
1097
+        }
1098
+
1099
+        // NOT A LINK SHARE
1100
+        else {
1101
+            if ($permissions !== null) {
1102
+                $permissions = (int) $permissions;
1103
+                $share->setPermissions($permissions);
1104
+            }
1105
+
1106
+            if ($expireDate === '') {
1107
+                $share->setExpirationDate(null);
1108
+            } elseif ($expireDate !== null) {
1109
+                try {
1110
+                    $expireDate = $this->parseDate($expireDate);
1111
+                } catch (\Exception $e) {
1112
+                    throw new OCSBadRequestException($e->getMessage(), $e);
1113
+                }
1114
+                $share->setExpirationDate($expireDate);
1115
+            }
1116
+        }
1117
+
1118
+        try {
1119
+            $share = $this->shareManager->updateShare($share);
1120
+        } catch (GenericShareException $e) {
1121
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1122
+            throw new OCSException($e->getHint(), $code);
1123
+        } catch (\Exception $e) {
1124
+            throw new OCSBadRequestException($e->getMessage(), $e);
1125
+        }
1126
+
1127
+        return new DataResponse($this->formatShare($share));
1128
+    }
1129
+
1130
+    /**
1131
+     * @NoAdminRequired
1132
+     */
1133
+    public function pendingShares(): DataResponse {
1134
+        $pendingShares = [];
1135
+
1136
+        $shareTypes = [
1137
+            IShare::TYPE_USER,
1138
+            IShare::TYPE_GROUP
1139
+        ];
1140
+
1141
+        foreach ($shareTypes as $shareType) {
1142
+            $shares = $this->shareManager->getSharedWith($this->currentUser, $shareType, null, -1, 0);
1143
+
1144
+            foreach ($shares as $share) {
1145
+                if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) {
1146
+                    $pendingShares[] = $share;
1147
+                }
1148
+            }
1149
+        }
1150
+
1151
+        $result = array_filter(array_map(function (IShare $share) {
1152
+            $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
1153
+            $nodes = $userFolder->getById($share->getNodeId());
1154
+            if (empty($nodes)) {
1155
+                // fallback to guessing the path
1156
+                $node = $userFolder->get($share->getTarget());
1157
+                if ($node === null || $share->getTarget() === '') {
1158
+                    return null;
1159
+                }
1160
+            } else {
1161
+                $node = $nodes[0];
1162
+            }
1163
+
1164
+            try {
1165
+                $formattedShare = $this->formatShare($share, $node);
1166
+                $formattedShare['status'] = $share->getStatus();
1167
+                $formattedShare['path'] = $share->getNode()->getName();
1168
+                $formattedShare['permissions'] = 0;
1169
+                return $formattedShare;
1170
+            } catch (NotFoundException $e) {
1171
+                return null;
1172
+            }
1173
+        }, $pendingShares), function ($entry) {
1174
+            return $entry !== null;
1175
+        });
1176
+
1177
+        return new DataResponse($result);
1178
+    }
1179
+
1180
+    /**
1181
+     * @NoAdminRequired
1182
+     *
1183
+     * @param string $id
1184
+     * @return DataResponse
1185
+     * @throws OCSNotFoundException
1186
+     * @throws OCSException
1187
+     * @throws OCSBadRequestException
1188
+     */
1189
+    public function acceptShare(string $id): DataResponse {
1190
+        try {
1191
+            $share = $this->getShareById($id);
1192
+        } catch (ShareNotFound $e) {
1193
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1194
+        }
1195
+
1196
+        if (!$this->canAccessShare($share)) {
1197
+            throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
1198
+        }
1199
+
1200
+        try {
1201
+            $this->shareManager->acceptShare($share, $this->currentUser);
1202
+        } catch (GenericShareException $e) {
1203
+            $code = $e->getCode() === 0 ? 403 : $e->getCode();
1204
+            throw new OCSException($e->getHint(), $code);
1205
+        } catch (\Exception $e) {
1206
+            throw new OCSBadRequestException($e->getMessage(), $e);
1207
+        }
1208
+
1209
+        return new DataResponse();
1210
+    }
1211
+
1212
+    /**
1213
+     * Does the user have read permission on the share
1214
+     *
1215
+     * @param \OCP\Share\IShare $share the share to check
1216
+     * @param boolean $checkGroups check groups as well?
1217
+     * @return boolean
1218
+     * @throws NotFoundException
1219
+     *
1220
+     * @suppress PhanUndeclaredClassMethod
1221
+     */
1222
+    protected function canAccessShare(\OCP\Share\IShare $share, bool $checkGroups = true): bool {
1223
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1224
+        if ($share->getPermissions() === 0) {
1225
+            return false;
1226
+        }
1227
+
1228
+        // Owner of the file and the sharer of the file can always get share
1229
+        if ($share->getShareOwner() === $this->currentUser
1230
+            || $share->getSharedBy() === $this->currentUser) {
1231
+            return true;
1232
+        }
1233
+
1234
+        // If the share is shared with you, you can access it!
1235
+        if ($share->getShareType() === IShare::TYPE_USER
1236
+            && $share->getSharedWith() === $this->currentUser) {
1237
+            return true;
1238
+        }
1239
+
1240
+        // Have reshare rights on the shared file/folder ?
1241
+        // Does the currentUser have access to the shared file?
1242
+        $userFolder = $this->rootFolder->getUserFolder($this->currentUser);
1243
+        $files = $userFolder->getById($share->getNodeId());
1244
+        if (!empty($files) && $this->shareProviderResharingRights($this->currentUser, $share, $files[0])) {
1245
+            return true;
1246
+        }
1247
+
1248
+        // If in the recipient group, you can see the share
1249
+        if ($checkGroups && $share->getShareType() === IShare::TYPE_GROUP) {
1250
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1251
+            $user = $this->userManager->get($this->currentUser);
1252
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1253
+                return true;
1254
+            }
1255
+        }
1256
+
1257
+        if ($share->getShareType() === IShare::TYPE_CIRCLE) {
1258
+            // TODO: have a sanity check like above?
1259
+            return true;
1260
+        }
1261
+
1262
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1263
+            try {
1264
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1265
+            } catch (QueryException $e) {
1266
+                return false;
1267
+            }
1268
+        }
1269
+
1270
+        return false;
1271
+    }
1272
+
1273
+    /**
1274
+     * Does the user have edit permission on the share
1275
+     *
1276
+     * @param \OCP\Share\IShare $share the share to check
1277
+     * @return boolean
1278
+     */
1279
+    protected function canEditShare(\OCP\Share\IShare $share): bool {
1280
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1281
+        if ($share->getPermissions() === 0) {
1282
+            return false;
1283
+        }
1284
+
1285
+        // The owner of the file and the creator of the share
1286
+        // can always edit the share
1287
+        if ($share->getShareOwner() === $this->currentUser ||
1288
+            $share->getSharedBy() === $this->currentUser
1289
+        ) {
1290
+            return true;
1291
+        }
1292
+
1293
+        //! we do NOT support some kind of `admin` in groups.
1294
+        //! You cannot edit shares shared to a group you're
1295
+        //! a member of if you're not the share owner or the file owner!
1296
+
1297
+        return false;
1298
+    }
1299
+
1300
+    /**
1301
+     * Does the user have delete permission on the share
1302
+     *
1303
+     * @param \OCP\Share\IShare $share the share to check
1304
+     * @return boolean
1305
+     */
1306
+    protected function canDeleteShare(\OCP\Share\IShare $share): bool {
1307
+        // A file with permissions 0 can't be accessed by us. So Don't show it
1308
+        if ($share->getPermissions() === 0) {
1309
+            return false;
1310
+        }
1311
+
1312
+        // if the user is the recipient, i can unshare
1313
+        // the share with self
1314
+        if ($share->getShareType() === IShare::TYPE_USER &&
1315
+            $share->getSharedWith() === $this->currentUser
1316
+        ) {
1317
+            return true;
1318
+        }
1319
+
1320
+        // The owner of the file and the creator of the share
1321
+        // can always delete the share
1322
+        if ($share->getShareOwner() === $this->currentUser ||
1323
+            $share->getSharedBy() === $this->currentUser
1324
+        ) {
1325
+            return true;
1326
+        }
1327
+
1328
+        return false;
1329
+    }
1330
+
1331
+    /**
1332
+     * Does the user have delete permission on the share
1333
+     * This differs from the canDeleteShare function as it only
1334
+     * remove the share for the current user. It does NOT
1335
+     * completely delete the share but only the mount point.
1336
+     * It can then be restored from the deleted shares section.
1337
+     *
1338
+     * @param \OCP\Share\IShare $share the share to check
1339
+     * @return boolean
1340
+     *
1341
+     * @suppress PhanUndeclaredClassMethod
1342
+     */
1343
+    protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
1344
+        if ($share->getShareType() !== IShare::TYPE_GROUP &&
1345
+            $share->getShareType() !== IShare::TYPE_ROOM
1346
+        ) {
1347
+            return false;
1348
+        }
1349
+
1350
+        if ($share->getShareOwner() === $this->currentUser ||
1351
+            $share->getSharedBy() === $this->currentUser
1352
+        ) {
1353
+            // Delete the whole share, not just for self
1354
+            return false;
1355
+        }
1356
+
1357
+        // If in the recipient group, you can delete the share from self
1358
+        if ($share->getShareType() === IShare::TYPE_GROUP) {
1359
+            $sharedWith = $this->groupManager->get($share->getSharedWith());
1360
+            $user = $this->userManager->get($this->currentUser);
1361
+            if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
1362
+                return true;
1363
+            }
1364
+        }
1365
+
1366
+        if ($share->getShareType() === IShare::TYPE_ROOM) {
1367
+            try {
1368
+                return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
1369
+            } catch (QueryException $e) {
1370
+                return false;
1371
+            }
1372
+        }
1373
+
1374
+        return false;
1375
+    }
1376
+
1377
+    /**
1378
+     * Make sure that the passed date is valid ISO 8601
1379
+     * So YYYY-MM-DD
1380
+     * If not throw an exception
1381
+     *
1382
+     * @param string $expireDate
1383
+     *
1384
+     * @throws \Exception
1385
+     * @return \DateTime
1386
+     */
1387
+    private function parseDate(string $expireDate): \DateTime {
1388
+        try {
1389
+            $date = new \DateTime($expireDate);
1390
+        } catch (\Exception $e) {
1391
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1392
+        }
1393
+
1394
+        if ($date === false) {
1395
+            throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
1396
+        }
1397
+
1398
+        $date->setTime(0, 0, 0);
1399
+
1400
+        return $date;
1401
+    }
1402
+
1403
+    /**
1404
+     * Since we have multiple providers but the OCS Share API v1 does
1405
+     * not support this we need to check all backends.
1406
+     *
1407
+     * @param string $id
1408
+     * @return \OCP\Share\IShare
1409
+     * @throws ShareNotFound
1410
+     */
1411
+    private function getShareById(string $id): IShare {
1412
+        $share = null;
1413
+
1414
+        // First check if it is an internal share.
1415
+        try {
1416
+            $share = $this->shareManager->getShareById('ocinternal:' . $id, $this->currentUser);
1417
+            return $share;
1418
+        } catch (ShareNotFound $e) {
1419
+            // Do nothing, just try the other share type
1420
+        }
1421
+
1422
+
1423
+        try {
1424
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_CIRCLE)) {
1425
+                $share = $this->shareManager->getShareById('ocCircleShare:' . $id, $this->currentUser);
1426
+                return $share;
1427
+            }
1428
+        } catch (ShareNotFound $e) {
1429
+            // Do nothing, just try the other share type
1430
+        }
1431
+
1432
+        try {
1433
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
1434
+                $share = $this->shareManager->getShareById('ocMailShare:' . $id, $this->currentUser);
1435
+                return $share;
1436
+            }
1437
+        } catch (ShareNotFound $e) {
1438
+            // Do nothing, just try the other share type
1439
+        }
1440
+
1441
+        try {
1442
+            $share = $this->shareManager->getShareById('ocRoomShare:' . $id, $this->currentUser);
1443
+            return $share;
1444
+        } catch (ShareNotFound $e) {
1445
+            // Do nothing, just try the other share type
1446
+        }
1447
+
1448
+        if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
1449
+            throw new ShareNotFound();
1450
+        }
1451
+        $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $this->currentUser);
1452
+
1453
+        return $share;
1454
+    }
1455
+
1456
+    /**
1457
+     * Lock a Node
1458
+     *
1459
+     * @param \OCP\Files\Node $node
1460
+     * @throws LockedException
1461
+     */
1462
+    private function lock(\OCP\Files\Node $node) {
1463
+        $node->lock(ILockingProvider::LOCK_SHARED);
1464
+        $this->lockedNode = $node;
1465
+    }
1466
+
1467
+    /**
1468
+     * Cleanup the remaining locks
1469
+     * @throws LockedException
1470
+     */
1471
+    public function cleanup() {
1472
+        if ($this->lockedNode !== null) {
1473
+            $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED);
1474
+        }
1475
+    }
1476
+
1477
+    /**
1478
+     * Returns the helper of ShareAPIController for room shares.
1479
+     *
1480
+     * If the Talk application is not enabled or the helper is not available
1481
+     * a QueryException is thrown instead.
1482
+     *
1483
+     * @return \OCA\Talk\Share\Helper\ShareAPIController
1484
+     * @throws QueryException
1485
+     */
1486
+    private function getRoomShareHelper() {
1487
+        if (!$this->appManager->isEnabledForUser('spreed')) {
1488
+            throw new QueryException();
1489
+        }
1490
+
1491
+        return $this->serverContainer->query('\OCA\Talk\Share\Helper\ShareAPIController');
1492
+    }
1493
+
1494
+
1495
+    /**
1496
+     * @param string $viewer
1497
+     * @param Node $node
1498
+     * @param bool $reShares
1499
+     *
1500
+     * @return IShare[]
1501
+     */
1502
+    private function getSharesFromNode(string $viewer, $node, bool $reShares): array {
1503
+        $providers = [
1504
+            IShare::TYPE_USER,
1505
+            IShare::TYPE_GROUP,
1506
+            IShare::TYPE_LINK,
1507
+            IShare::TYPE_EMAIL,
1508
+            IShare::TYPE_EMAIL,
1509
+            IShare::TYPE_CIRCLE,
1510
+            IShare::TYPE_ROOM
1511
+        ];
1512
+
1513
+        // Should we assume that the (currentUser) viewer is the owner of the node !?
1514
+        $shares = [];
1515
+        foreach ($providers as $provider) {
1516
+            if (!$this->shareManager->shareProviderExists($provider)) {
1517
+                continue;
1518
+            }
1519
+
1520
+            $providerShares =
1521
+                $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
1522
+            $shares = array_merge($shares, $providerShares);
1523
+        }
1524
+
1525
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1526
+            $federatedShares = $this->shareManager->getSharesBy(
1527
+                $this->currentUser, IShare::TYPE_REMOTE, $node, $reShares, -1, 0
1528
+            );
1529
+            $shares = array_merge($shares, $federatedShares);
1530
+        }
1531
+
1532
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1533
+            $federatedShares = $this->shareManager->getSharesBy(
1534
+                $this->currentUser, IShare::TYPE_REMOTE_GROUP, $node, $reShares, -1, 0
1535
+            );
1536
+            $shares = array_merge($shares, $federatedShares);
1537
+        }
1538
+
1539
+        return $shares;
1540
+    }
1541
+
1542
+
1543
+    /**
1544
+     * @param Node $node
1545
+     *
1546
+     * @throws SharingRightsException
1547
+     */
1548
+    private function confirmSharingRights(Node $node): void {
1549
+        if (!$this->hasResharingRights($this->currentUser, $node)) {
1550
+            throw new SharingRightsException('no sharing rights on this item');
1551
+        }
1552
+    }
1553
+
1554
+
1555
+    /**
1556
+     * @param string $viewer
1557
+     * @param Node $node
1558
+     *
1559
+     * @return bool
1560
+     */
1561
+    private function hasResharingRights($viewer, $node): bool {
1562
+        if ($viewer === $node->getOwner()->getUID()) {
1563
+            return true;
1564
+        }
1565
+
1566
+        foreach ([$node, $node->getParent()] as $node) {
1567
+            $shares = $this->getSharesFromNode($viewer, $node, true);
1568
+            foreach ($shares as $share) {
1569
+                try {
1570
+                    if ($this->shareProviderResharingRights($viewer, $share, $node)) {
1571
+                        return true;
1572
+                    }
1573
+                } catch (InvalidPathException | NotFoundException $e) {
1574
+                }
1575
+            }
1576
+        }
1577
+
1578
+        return false;
1579
+    }
1580
+
1581
+
1582
+    /**
1583
+     * Returns if we can find resharing rights in an IShare object for a specific user.
1584
+     *
1585
+     * @suppress PhanUndeclaredClassMethod
1586
+     *
1587
+     * @param string $userId
1588
+     * @param IShare $share
1589
+     * @param Node $node
1590
+     *
1591
+     * @return bool
1592
+     * @throws NotFoundException
1593
+     * @throws InvalidPathException
1594
+     */
1595
+    private function shareProviderResharingRights(string $userId, IShare $share, $node): bool {
1596
+        if ($share->getShareOwner() === $userId) {
1597
+            return true;
1598
+        }
1599
+
1600
+        // we check that current user have parent resharing rights on the current file
1601
+        if ($node !== null && ($node->getPermissions() & Constants::PERMISSION_SHARE) !== 0) {
1602
+            return true;
1603
+        }
1604
+
1605
+        if ((\OCP\Constants::PERMISSION_SHARE & $share->getPermissions()) === 0) {
1606
+            return false;
1607
+        }
1608
+
1609
+        if ($share->getShareType() === IShare::TYPE_USER && $share->getSharedWith() === $userId) {
1610
+            return true;
1611
+        }
1612
+
1613
+        if ($share->getShareType() === IShare::TYPE_GROUP && $this->groupManager->isInGroup($userId, $share->getSharedWith())) {
1614
+            return true;
1615
+        }
1616
+
1617
+        if ($share->getShareType() === IShare::TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
1618
+            && class_exists('\OCA\Circles\Api\v1\Circles')) {
1619
+            $hasCircleId = (substr($share->getSharedWith(), -1) === ']');
1620
+            $shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
1621
+            $shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
1622
+            if (is_bool($shareWithLength)) {
1623
+                $shareWithLength = -1;
1624
+            }
1625
+            $sharedWith = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
1626
+            try {
1627
+                $member = \OCA\Circles\Api\v1\Circles::getMember($sharedWith, $userId, 1);
1628
+                if ($member->getLevel() >= 4) {
1629
+                    return true;
1630
+                }
1631
+                return false;
1632
+            } catch (QueryException $e) {
1633
+                return false;
1634
+            }
1635
+        }
1636
+
1637
+        return false;
1638
+    }
1639
+
1640
+    /**
1641
+     * Get all the shares for the current user
1642
+     *
1643
+     * @param Node|null $path
1644
+     * @param boolean $reshares
1645
+     * @return IShare[]
1646
+     */
1647
+    private function getAllShares(?Node $path = null, bool $reshares = false) {
1648
+        // Get all shares
1649
+        $userShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_USER, $path, $reshares, -1, 0);
1650
+        $groupShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_GROUP, $path, $reshares, -1, 0);
1651
+        $linkShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_LINK, $path, $reshares, -1, 0);
1652
+
1653
+        // EMAIL SHARES
1654
+        $mailShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_EMAIL, $path, $reshares, -1, 0);
1655
+
1656
+        // CIRCLE SHARES
1657
+        $circleShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_CIRCLE, $path, $reshares, -1, 0);
1658
+
1659
+        // TALK SHARES
1660
+        $roomShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_ROOM, $path, $reshares, -1, 0);
1661
+
1662
+        // FEDERATION
1663
+        if ($this->shareManager->outgoingServer2ServerSharesAllowed()) {
1664
+            $federatedShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_REMOTE, $path, $reshares, -1, 0);
1665
+        } else {
1666
+            $federatedShares = [];
1667
+        }
1668
+        if ($this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
1669
+            $federatedGroupShares = $this->shareManager->getSharesBy($this->currentUser, IShare::TYPE_REMOTE_GROUP, $path, $reshares, -1, 0);
1670
+        } else {
1671
+            $federatedGroupShares = [];
1672
+        }
1673
+
1674
+        return array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares, $roomShares, $federatedShares, $federatedGroupShares);
1675
+    }
1676
+
1677
+
1678
+    /**
1679
+     * merging already formatted shares.
1680
+     * We'll make an associative array to easily detect duplicate Ids.
1681
+     * Keys _needs_ to be removed after all shares are retrieved and merged.
1682
+     *
1683
+     * @param array $shares
1684
+     * @param array $newShares
1685
+     */
1686
+    private function mergeFormattedShares(array &$shares, array $newShares) {
1687
+        foreach ($newShares as $newShare) {
1688
+            if (!array_key_exists($newShare['id'], $shares)) {
1689
+                $shares[$newShare['id']] = $newShare;
1690
+            }
1691
+        }
1692
+    }
1693 1693
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareesAPIController.php 1 patch
Indentation   +369 added lines, -369 removed lines patch added patch discarded remove patch
@@ -54,373 +54,373 @@
 block discarded – undo
54 54
 
55 55
 class ShareesAPIController extends OCSController {
56 56
 
57
-	/** @var string */
58
-	protected $userId;
59
-
60
-	/** @var IConfig */
61
-	protected $config;
62
-
63
-	/** @var IURLGenerator */
64
-	protected $urlGenerator;
65
-
66
-	/** @var IManager */
67
-	protected $shareManager;
68
-
69
-	/** @var int */
70
-	protected $offset = 0;
71
-
72
-	/** @var int */
73
-	protected $limit = 10;
74
-
75
-	/** @var array */
76
-	protected $result = [
77
-		'exact' => [
78
-			'users' => [],
79
-			'groups' => [],
80
-			'remotes' => [],
81
-			'remote_groups' => [],
82
-			'emails' => [],
83
-			'circles' => [],
84
-			'rooms' => [],
85
-		],
86
-		'users' => [],
87
-		'groups' => [],
88
-		'remotes' => [],
89
-		'remote_groups' => [],
90
-		'emails' => [],
91
-		'lookup' => [],
92
-		'circles' => [],
93
-		'rooms' => [],
94
-		'lookupEnabled' => false,
95
-	];
96
-
97
-	protected $reachedEndFor = [];
98
-	/** @var ISearch */
99
-	private $collaboratorSearch;
100
-
101
-	/**
102
-	 * @param string $UserId
103
-	 * @param string $appName
104
-	 * @param IRequest $request
105
-	 * @param IConfig $config
106
-	 * @param IURLGenerator $urlGenerator
107
-	 * @param IManager $shareManager
108
-	 * @param ISearch $collaboratorSearch
109
-	 */
110
-	public function __construct(
111
-		$UserId,
112
-		string $appName,
113
-		IRequest $request,
114
-		IConfig $config,
115
-		IURLGenerator $urlGenerator,
116
-		IManager $shareManager,
117
-		ISearch $collaboratorSearch
118
-	) {
119
-		parent::__construct($appName, $request);
120
-		$this->userId = $UserId;
121
-		$this->config = $config;
122
-		$this->urlGenerator = $urlGenerator;
123
-		$this->shareManager = $shareManager;
124
-		$this->collaboratorSearch = $collaboratorSearch;
125
-	}
126
-
127
-	/**
128
-	 * @NoAdminRequired
129
-	 *
130
-	 * @param string $search
131
-	 * @param string $itemType
132
-	 * @param int $page
133
-	 * @param int $perPage
134
-	 * @param int|int[] $shareType
135
-	 * @param bool $lookup
136
-	 * @return DataResponse
137
-	 * @throws OCSBadRequestException
138
-	 */
139
-	public function search(string $search = '', string $itemType = null, int $page = 1, int $perPage = 200, $shareType = null, bool $lookup = true): DataResponse {
140
-
141
-		// only search for string larger than a given threshold
142
-		$threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
143
-		if (strlen($search) < $threshold) {
144
-			return new DataResponse($this->result);
145
-		}
146
-
147
-		// never return more than the max. number of results configured in the config.php
148
-		$maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
149
-		if ($maxResults > 0) {
150
-			$perPage = min($perPage, $maxResults);
151
-		}
152
-		if ($perPage <= 0) {
153
-			throw new OCSBadRequestException('Invalid perPage argument');
154
-		}
155
-		if ($page <= 0) {
156
-			throw new OCSBadRequestException('Invalid page');
157
-		}
158
-
159
-		$shareTypes = [
160
-			IShare::TYPE_USER,
161
-		];
162
-
163
-		if ($itemType === null) {
164
-			throw new OCSBadRequestException('Missing itemType');
165
-		} elseif ($itemType === 'file' || $itemType === 'folder') {
166
-			if ($this->shareManager->allowGroupSharing()) {
167
-				$shareTypes[] = IShare::TYPE_GROUP;
168
-			}
169
-
170
-			if ($this->isRemoteSharingAllowed($itemType)) {
171
-				$shareTypes[] = IShare::TYPE_REMOTE;
172
-			}
173
-
174
-			if ($this->isRemoteGroupSharingAllowed($itemType)) {
175
-				$shareTypes[] = IShare::TYPE_REMOTE_GROUP;
176
-			}
177
-
178
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
179
-				$shareTypes[] = IShare::TYPE_EMAIL;
180
-			}
181
-
182
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) {
183
-				$shareTypes[] = IShare::TYPE_ROOM;
184
-			}
185
-		} else {
186
-			$shareTypes[] = IShare::TYPE_GROUP;
187
-			$shareTypes[] = IShare::TYPE_EMAIL;
188
-		}
189
-
190
-		// FIXME: DI
191
-		if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
192
-			$shareTypes[] = IShare::TYPE_CIRCLE;
193
-		}
194
-
195
-		if ($shareType !== null && is_array($shareType)) {
196
-			$shareTypes = array_intersect($shareTypes, $shareType);
197
-		} elseif (is_numeric($shareType)) {
198
-			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
199
-		}
200
-		sort($shareTypes);
201
-
202
-		$this->limit = (int) $perPage;
203
-		$this->offset = $perPage * ($page - 1);
204
-
205
-		// In global scale mode we always search the loogup server
206
-		if ($this->config->getSystemValueBool('gs.enabled', false)) {
207
-			$lookup = true;
208
-			$this->result['lookupEnabled'] = true;
209
-		} else {
210
-			$this->result['lookupEnabled'] = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes';
211
-		}
212
-
213
-		list($result, $hasMoreResults) = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset);
214
-
215
-		// extra treatment for 'exact' subarray, with a single merge expected keys might be lost
216
-		if (isset($result['exact'])) {
217
-			$result['exact'] = array_merge($this->result['exact'], $result['exact']);
218
-		}
219
-		$this->result = array_merge($this->result, $result);
220
-		$response = new DataResponse($this->result);
221
-
222
-		if ($hasMoreResults) {
223
-			$response->addHeader('Link', $this->getPaginationLink($page, [
224
-				'search' => $search,
225
-				'itemType' => $itemType,
226
-				'shareType' => $shareTypes,
227
-				'perPage' => $perPage,
228
-			]));
229
-		}
230
-
231
-		return $response;
232
-	}
233
-
234
-	/**
235
-	 * @param string $user
236
-	 * @param int $shareType
237
-	 *
238
-	 * @return Generator<array<string>>
239
-	 */
240
-	private function getAllShareesByType(string $user, int $shareType): Generator {
241
-		$offset = 0;
242
-		$pageSize = 50;
243
-
244
-		while (count($page = $this->shareManager->getSharesBy(
245
-			$user,
246
-			$shareType,
247
-			null,
248
-			false,
249
-			$pageSize,
250
-			$offset
251
-		))) {
252
-			foreach ($page as $share) {
253
-				yield [$share->getSharedWith(), $share->getSharedWithDisplayName() ?? $share->getSharedWith()];
254
-			}
255
-
256
-			$offset += $pageSize;
257
-		}
258
-	}
259
-
260
-	private function sortShareesByFrequency(array $sharees): array {
261
-		usort($sharees, function (array $s1, array $s2) {
262
-			return $s2['count'] - $s1['count'];
263
-		});
264
-		return $sharees;
265
-	}
266
-
267
-	private $searchResultTypeMap = [
268
-		IShare::TYPE_USER => 'users',
269
-		IShare::TYPE_GROUP => 'groups',
270
-		IShare::TYPE_REMOTE => 'remotes',
271
-		IShare::TYPE_REMOTE_GROUP => 'remote_groups',
272
-		IShare::TYPE_EMAIL => 'emails',
273
-	];
274
-
275
-	private function getAllSharees(string $user, array $shareTypes): ISearchResult {
276
-		$result = [];
277
-		foreach ($shareTypes as $shareType) {
278
-			$sharees = $this->getAllShareesByType($user, $shareType);
279
-			$shareTypeResults = [];
280
-			foreach ($sharees as list($sharee, $displayname)) {
281
-				if (!isset($this->searchResultTypeMap[$shareType])) {
282
-					continue;
283
-				}
284
-
285
-				if (!isset($shareTypeResults[$sharee])) {
286
-					$shareTypeResults[$sharee] = [
287
-						'count' => 1,
288
-						'label' => $displayname,
289
-						'value' => [
290
-							'shareType' => $shareType,
291
-							'shareWith' => $sharee,
292
-						],
293
-					];
294
-				} else {
295
-					$shareTypeResults[$sharee]['count']++;
296
-				}
297
-			}
298
-			$result = array_merge($result, array_values($shareTypeResults));
299
-		}
300
-
301
-		$top5 = array_slice(
302
-			$this->sortShareesByFrequency($result),
303
-			0,
304
-			5
305
-		);
306
-
307
-		$searchResult = new SearchResult();
308
-		foreach ($this->searchResultTypeMap as $int => $str) {
309
-			$searchResult->addResultSet(new SearchResultType($str), [], []);
310
-			foreach ($top5 as $x) {
311
-				if ($x['value']['shareType'] === $int) {
312
-					$searchResult->addResultSet(new SearchResultType($str), [], [$x]);
313
-				}
314
-			}
315
-		}
316
-		return $searchResult;
317
-	}
318
-
319
-	/**
320
-	 * @NoAdminRequired
321
-	 *
322
-	 * @param string $itemType
323
-	 * @return DataResponse
324
-	 * @throws OCSBadRequestException
325
-	 */
326
-	public function findRecommended(string $itemType = null, $shareType = null): DataResponse {
327
-		$shareTypes = [
328
-			IShare::TYPE_USER,
329
-		];
330
-
331
-		if ($itemType === null) {
332
-			throw new OCSBadRequestException('Missing itemType');
333
-		} elseif ($itemType === 'file' || $itemType === 'folder') {
334
-			if ($this->shareManager->allowGroupSharing()) {
335
-				$shareTypes[] = IShare::TYPE_GROUP;
336
-			}
337
-
338
-			if ($this->isRemoteSharingAllowed($itemType)) {
339
-				$shareTypes[] = IShare::TYPE_REMOTE;
340
-			}
341
-
342
-			if ($this->isRemoteGroupSharingAllowed($itemType)) {
343
-				$shareTypes[] = IShare::TYPE_REMOTE_GROUP;
344
-			}
345
-
346
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
347
-				$shareTypes[] = IShare::TYPE_EMAIL;
348
-			}
349
-
350
-			if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) {
351
-				$shareTypes[] = IShare::TYPE_ROOM;
352
-			}
353
-		} else {
354
-			$shareTypes[] = IShare::TYPE_GROUP;
355
-			$shareTypes[] = IShare::TYPE_EMAIL;
356
-		}
357
-
358
-		// FIXME: DI
359
-		if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
360
-			$shareTypes[] = IShare::TYPE_CIRCLE;
361
-		}
362
-
363
-		if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
364
-			$shareTypes = array_intersect($shareTypes, $_GET['shareType']);
365
-			sort($shareTypes);
366
-		} elseif (is_numeric($shareType)) {
367
-			$shareTypes = array_intersect($shareTypes, [(int) $shareType]);
368
-			sort($shareTypes);
369
-		}
370
-
371
-		return new DataResponse(
372
-			$this->getAllSharees($this->userId, $shareTypes)->asArray()
373
-		);
374
-	}
375
-
376
-	/**
377
-	 * Method to get out the static call for better testing
378
-	 *
379
-	 * @param string $itemType
380
-	 * @return bool
381
-	 */
382
-	protected function isRemoteSharingAllowed(string $itemType): bool {
383
-		try {
384
-			// FIXME: static foo makes unit testing unnecessarily difficult
385
-			$backend = \OC\Share\Share::getBackend($itemType);
386
-			return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE);
387
-		} catch (\Exception $e) {
388
-			return false;
389
-		}
390
-	}
391
-
392
-	protected function isRemoteGroupSharingAllowed(string $itemType): bool {
393
-		try {
394
-			// FIXME: static foo makes unit testing unnecessarily difficult
395
-			$backend = \OC\Share\Share::getBackend($itemType);
396
-			return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE_GROUP);
397
-		} catch (\Exception $e) {
398
-			return false;
399
-		}
400
-	}
401
-
402
-
403
-	/**
404
-	 * Generates a bunch of pagination links for the current page
405
-	 *
406
-	 * @param int $page Current page
407
-	 * @param array $params Parameters for the URL
408
-	 * @return string
409
-	 */
410
-	protected function getPaginationLink(int $page, array $params): string {
411
-		if ($this->isV2()) {
412
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
413
-		} else {
414
-			$url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
415
-		}
416
-		$params['page'] = $page + 1;
417
-		return '<' . $url . http_build_query($params) . '>; rel="next"';
418
-	}
419
-
420
-	/**
421
-	 * @return bool
422
-	 */
423
-	protected function isV2(): bool {
424
-		return $this->request->getScriptName() === '/ocs/v2.php';
425
-	}
57
+    /** @var string */
58
+    protected $userId;
59
+
60
+    /** @var IConfig */
61
+    protected $config;
62
+
63
+    /** @var IURLGenerator */
64
+    protected $urlGenerator;
65
+
66
+    /** @var IManager */
67
+    protected $shareManager;
68
+
69
+    /** @var int */
70
+    protected $offset = 0;
71
+
72
+    /** @var int */
73
+    protected $limit = 10;
74
+
75
+    /** @var array */
76
+    protected $result = [
77
+        'exact' => [
78
+            'users' => [],
79
+            'groups' => [],
80
+            'remotes' => [],
81
+            'remote_groups' => [],
82
+            'emails' => [],
83
+            'circles' => [],
84
+            'rooms' => [],
85
+        ],
86
+        'users' => [],
87
+        'groups' => [],
88
+        'remotes' => [],
89
+        'remote_groups' => [],
90
+        'emails' => [],
91
+        'lookup' => [],
92
+        'circles' => [],
93
+        'rooms' => [],
94
+        'lookupEnabled' => false,
95
+    ];
96
+
97
+    protected $reachedEndFor = [];
98
+    /** @var ISearch */
99
+    private $collaboratorSearch;
100
+
101
+    /**
102
+     * @param string $UserId
103
+     * @param string $appName
104
+     * @param IRequest $request
105
+     * @param IConfig $config
106
+     * @param IURLGenerator $urlGenerator
107
+     * @param IManager $shareManager
108
+     * @param ISearch $collaboratorSearch
109
+     */
110
+    public function __construct(
111
+        $UserId,
112
+        string $appName,
113
+        IRequest $request,
114
+        IConfig $config,
115
+        IURLGenerator $urlGenerator,
116
+        IManager $shareManager,
117
+        ISearch $collaboratorSearch
118
+    ) {
119
+        parent::__construct($appName, $request);
120
+        $this->userId = $UserId;
121
+        $this->config = $config;
122
+        $this->urlGenerator = $urlGenerator;
123
+        $this->shareManager = $shareManager;
124
+        $this->collaboratorSearch = $collaboratorSearch;
125
+    }
126
+
127
+    /**
128
+     * @NoAdminRequired
129
+     *
130
+     * @param string $search
131
+     * @param string $itemType
132
+     * @param int $page
133
+     * @param int $perPage
134
+     * @param int|int[] $shareType
135
+     * @param bool $lookup
136
+     * @return DataResponse
137
+     * @throws OCSBadRequestException
138
+     */
139
+    public function search(string $search = '', string $itemType = null, int $page = 1, int $perPage = 200, $shareType = null, bool $lookup = true): DataResponse {
140
+
141
+        // only search for string larger than a given threshold
142
+        $threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0);
143
+        if (strlen($search) < $threshold) {
144
+            return new DataResponse($this->result);
145
+        }
146
+
147
+        // never return more than the max. number of results configured in the config.php
148
+        $maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0);
149
+        if ($maxResults > 0) {
150
+            $perPage = min($perPage, $maxResults);
151
+        }
152
+        if ($perPage <= 0) {
153
+            throw new OCSBadRequestException('Invalid perPage argument');
154
+        }
155
+        if ($page <= 0) {
156
+            throw new OCSBadRequestException('Invalid page');
157
+        }
158
+
159
+        $shareTypes = [
160
+            IShare::TYPE_USER,
161
+        ];
162
+
163
+        if ($itemType === null) {
164
+            throw new OCSBadRequestException('Missing itemType');
165
+        } elseif ($itemType === 'file' || $itemType === 'folder') {
166
+            if ($this->shareManager->allowGroupSharing()) {
167
+                $shareTypes[] = IShare::TYPE_GROUP;
168
+            }
169
+
170
+            if ($this->isRemoteSharingAllowed($itemType)) {
171
+                $shareTypes[] = IShare::TYPE_REMOTE;
172
+            }
173
+
174
+            if ($this->isRemoteGroupSharingAllowed($itemType)) {
175
+                $shareTypes[] = IShare::TYPE_REMOTE_GROUP;
176
+            }
177
+
178
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
179
+                $shareTypes[] = IShare::TYPE_EMAIL;
180
+            }
181
+
182
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) {
183
+                $shareTypes[] = IShare::TYPE_ROOM;
184
+            }
185
+        } else {
186
+            $shareTypes[] = IShare::TYPE_GROUP;
187
+            $shareTypes[] = IShare::TYPE_EMAIL;
188
+        }
189
+
190
+        // FIXME: DI
191
+        if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
192
+            $shareTypes[] = IShare::TYPE_CIRCLE;
193
+        }
194
+
195
+        if ($shareType !== null && is_array($shareType)) {
196
+            $shareTypes = array_intersect($shareTypes, $shareType);
197
+        } elseif (is_numeric($shareType)) {
198
+            $shareTypes = array_intersect($shareTypes, [(int) $shareType]);
199
+        }
200
+        sort($shareTypes);
201
+
202
+        $this->limit = (int) $perPage;
203
+        $this->offset = $perPage * ($page - 1);
204
+
205
+        // In global scale mode we always search the loogup server
206
+        if ($this->config->getSystemValueBool('gs.enabled', false)) {
207
+            $lookup = true;
208
+            $this->result['lookupEnabled'] = true;
209
+        } else {
210
+            $this->result['lookupEnabled'] = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes';
211
+        }
212
+
213
+        list($result, $hasMoreResults) = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset);
214
+
215
+        // extra treatment for 'exact' subarray, with a single merge expected keys might be lost
216
+        if (isset($result['exact'])) {
217
+            $result['exact'] = array_merge($this->result['exact'], $result['exact']);
218
+        }
219
+        $this->result = array_merge($this->result, $result);
220
+        $response = new DataResponse($this->result);
221
+
222
+        if ($hasMoreResults) {
223
+            $response->addHeader('Link', $this->getPaginationLink($page, [
224
+                'search' => $search,
225
+                'itemType' => $itemType,
226
+                'shareType' => $shareTypes,
227
+                'perPage' => $perPage,
228
+            ]));
229
+        }
230
+
231
+        return $response;
232
+    }
233
+
234
+    /**
235
+     * @param string $user
236
+     * @param int $shareType
237
+     *
238
+     * @return Generator<array<string>>
239
+     */
240
+    private function getAllShareesByType(string $user, int $shareType): Generator {
241
+        $offset = 0;
242
+        $pageSize = 50;
243
+
244
+        while (count($page = $this->shareManager->getSharesBy(
245
+            $user,
246
+            $shareType,
247
+            null,
248
+            false,
249
+            $pageSize,
250
+            $offset
251
+        ))) {
252
+            foreach ($page as $share) {
253
+                yield [$share->getSharedWith(), $share->getSharedWithDisplayName() ?? $share->getSharedWith()];
254
+            }
255
+
256
+            $offset += $pageSize;
257
+        }
258
+    }
259
+
260
+    private function sortShareesByFrequency(array $sharees): array {
261
+        usort($sharees, function (array $s1, array $s2) {
262
+            return $s2['count'] - $s1['count'];
263
+        });
264
+        return $sharees;
265
+    }
266
+
267
+    private $searchResultTypeMap = [
268
+        IShare::TYPE_USER => 'users',
269
+        IShare::TYPE_GROUP => 'groups',
270
+        IShare::TYPE_REMOTE => 'remotes',
271
+        IShare::TYPE_REMOTE_GROUP => 'remote_groups',
272
+        IShare::TYPE_EMAIL => 'emails',
273
+    ];
274
+
275
+    private function getAllSharees(string $user, array $shareTypes): ISearchResult {
276
+        $result = [];
277
+        foreach ($shareTypes as $shareType) {
278
+            $sharees = $this->getAllShareesByType($user, $shareType);
279
+            $shareTypeResults = [];
280
+            foreach ($sharees as list($sharee, $displayname)) {
281
+                if (!isset($this->searchResultTypeMap[$shareType])) {
282
+                    continue;
283
+                }
284
+
285
+                if (!isset($shareTypeResults[$sharee])) {
286
+                    $shareTypeResults[$sharee] = [
287
+                        'count' => 1,
288
+                        'label' => $displayname,
289
+                        'value' => [
290
+                            'shareType' => $shareType,
291
+                            'shareWith' => $sharee,
292
+                        ],
293
+                    ];
294
+                } else {
295
+                    $shareTypeResults[$sharee]['count']++;
296
+                }
297
+            }
298
+            $result = array_merge($result, array_values($shareTypeResults));
299
+        }
300
+
301
+        $top5 = array_slice(
302
+            $this->sortShareesByFrequency($result),
303
+            0,
304
+            5
305
+        );
306
+
307
+        $searchResult = new SearchResult();
308
+        foreach ($this->searchResultTypeMap as $int => $str) {
309
+            $searchResult->addResultSet(new SearchResultType($str), [], []);
310
+            foreach ($top5 as $x) {
311
+                if ($x['value']['shareType'] === $int) {
312
+                    $searchResult->addResultSet(new SearchResultType($str), [], [$x]);
313
+                }
314
+            }
315
+        }
316
+        return $searchResult;
317
+    }
318
+
319
+    /**
320
+     * @NoAdminRequired
321
+     *
322
+     * @param string $itemType
323
+     * @return DataResponse
324
+     * @throws OCSBadRequestException
325
+     */
326
+    public function findRecommended(string $itemType = null, $shareType = null): DataResponse {
327
+        $shareTypes = [
328
+            IShare::TYPE_USER,
329
+        ];
330
+
331
+        if ($itemType === null) {
332
+            throw new OCSBadRequestException('Missing itemType');
333
+        } elseif ($itemType === 'file' || $itemType === 'folder') {
334
+            if ($this->shareManager->allowGroupSharing()) {
335
+                $shareTypes[] = IShare::TYPE_GROUP;
336
+            }
337
+
338
+            if ($this->isRemoteSharingAllowed($itemType)) {
339
+                $shareTypes[] = IShare::TYPE_REMOTE;
340
+            }
341
+
342
+            if ($this->isRemoteGroupSharingAllowed($itemType)) {
343
+                $shareTypes[] = IShare::TYPE_REMOTE_GROUP;
344
+            }
345
+
346
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_EMAIL)) {
347
+                $shareTypes[] = IShare::TYPE_EMAIL;
348
+            }
349
+
350
+            if ($this->shareManager->shareProviderExists(IShare::TYPE_ROOM)) {
351
+                $shareTypes[] = IShare::TYPE_ROOM;
352
+            }
353
+        } else {
354
+            $shareTypes[] = IShare::TYPE_GROUP;
355
+            $shareTypes[] = IShare::TYPE_EMAIL;
356
+        }
357
+
358
+        // FIXME: DI
359
+        if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
360
+            $shareTypes[] = IShare::TYPE_CIRCLE;
361
+        }
362
+
363
+        if (isset($_GET['shareType']) && is_array($_GET['shareType'])) {
364
+            $shareTypes = array_intersect($shareTypes, $_GET['shareType']);
365
+            sort($shareTypes);
366
+        } elseif (is_numeric($shareType)) {
367
+            $shareTypes = array_intersect($shareTypes, [(int) $shareType]);
368
+            sort($shareTypes);
369
+        }
370
+
371
+        return new DataResponse(
372
+            $this->getAllSharees($this->userId, $shareTypes)->asArray()
373
+        );
374
+    }
375
+
376
+    /**
377
+     * Method to get out the static call for better testing
378
+     *
379
+     * @param string $itemType
380
+     * @return bool
381
+     */
382
+    protected function isRemoteSharingAllowed(string $itemType): bool {
383
+        try {
384
+            // FIXME: static foo makes unit testing unnecessarily difficult
385
+            $backend = \OC\Share\Share::getBackend($itemType);
386
+            return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE);
387
+        } catch (\Exception $e) {
388
+            return false;
389
+        }
390
+    }
391
+
392
+    protected function isRemoteGroupSharingAllowed(string $itemType): bool {
393
+        try {
394
+            // FIXME: static foo makes unit testing unnecessarily difficult
395
+            $backend = \OC\Share\Share::getBackend($itemType);
396
+            return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE_GROUP);
397
+        } catch (\Exception $e) {
398
+            return false;
399
+        }
400
+    }
401
+
402
+
403
+    /**
404
+     * Generates a bunch of pagination links for the current page
405
+     *
406
+     * @param int $page Current page
407
+     * @param array $params Parameters for the URL
408
+     * @return string
409
+     */
410
+    protected function getPaginationLink(int $page, array $params): string {
411
+        if ($this->isV2()) {
412
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?';
413
+        } else {
414
+            $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?';
415
+        }
416
+        $params['page'] = $page + 1;
417
+        return '<' . $url . http_build_query($params) . '>; rel="next"';
418
+    }
419
+
420
+    /**
421
+     * @return bool
422
+     */
423
+    protected function isV2(): bool {
424
+        return $this->request->getScriptName() === '/ocs/v2.php';
425
+    }
426 426
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/SharedMount.php 1 patch
Indentation   +224 added lines, -224 removed lines patch added patch discarded remove patch
@@ -41,228 +41,228 @@
 block discarded – undo
41 41
  * Shared mount points can be moved by the user
42 42
  */
43 43
 class SharedMount extends MountPoint implements MoveableMount {
44
-	/**
45
-	 * @var \OCA\Files_Sharing\SharedStorage $storage
46
-	 */
47
-	protected $storage = null;
48
-
49
-	/**
50
-	 * @var \OC\Files\View
51
-	 */
52
-	private $recipientView;
53
-
54
-	/**
55
-	 * @var string
56
-	 */
57
-	private $user;
58
-
59
-	/** @var \OCP\Share\IShare */
60
-	private $superShare;
61
-
62
-	/** @var \OCP\Share\IShare[] */
63
-	private $groupedShares;
64
-
65
-	/**
66
-	 * @param string $storage
67
-	 * @param SharedMount[] $mountpoints
68
-	 * @param array $arguments
69
-	 * @param IStorageFactory $loader
70
-	 * @param View $recipientView
71
-	 */
72
-	public function __construct($storage, array $mountpoints, $arguments, IStorageFactory $loader, View $recipientView, CappedMemoryCache $folderExistCache) {
73
-		$this->user = $arguments['user'];
74
-		$this->recipientView = $recipientView;
75
-
76
-		$this->superShare = $arguments['superShare'];
77
-		$this->groupedShares = $arguments['groupedShares'];
78
-
79
-		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints, $folderExistCache);
80
-		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
81
-		parent::__construct($storage, $absMountPoint, $arguments, $loader);
82
-	}
83
-
84
-	/**
85
-	 * check if the parent folder exists otherwise move the mount point up
86
-	 *
87
-	 * @param \OCP\Share\IShare $share
88
-	 * @param SharedMount[] $mountpoints
89
-	 * @return string
90
-	 */
91
-	private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints, CappedMemoryCache $folderExistCache) {
92
-		$mountPoint = basename($share->getTarget());
93
-		$parent = dirname($share->getTarget());
94
-
95
-		$event = new VerifyMountPointEvent($share, $this->recipientView, $parent);
96
-		/** @var IEventDispatcher $dispatcher */
97
-		$dispatcher = \OC::$server->query(IEventDispatcher::class);
98
-		$dispatcher->dispatchTyped($event);
99
-		$parent = $event->getParent();
100
-
101
-		if ($folderExistCache->hasKey($parent)) {
102
-			$parentExists = $folderExistCache->get($parent);
103
-		} else {
104
-			$parentExists = $this->recipientView->is_dir($parent);
105
-			$folderExistCache->set($parent, $parentExists);
106
-		}
107
-		if (!$parentExists) {
108
-			$parent = Helper::getShareFolder($this->recipientView);
109
-		}
110
-
111
-		$newMountPoint = $this->generateUniqueTarget(
112
-			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
113
-			$this->recipientView,
114
-			$mountpoints
115
-		);
116
-
117
-		if ($newMountPoint !== $share->getTarget()) {
118
-			$this->updateFileTarget($newMountPoint, $share);
119
-		}
120
-
121
-		return $newMountPoint;
122
-	}
123
-
124
-	/**
125
-	 * update fileTarget in the database if the mount point changed
126
-	 *
127
-	 * @param string $newPath
128
-	 * @param \OCP\Share\IShare $share
129
-	 * @return bool
130
-	 */
131
-	private function updateFileTarget($newPath, &$share) {
132
-		$share->setTarget($newPath);
133
-
134
-		foreach ($this->groupedShares as $tmpShare) {
135
-			$tmpShare->setTarget($newPath);
136
-			\OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
137
-		}
138
-	}
139
-
140
-
141
-	/**
142
-	 * @param string $path
143
-	 * @param View $view
144
-	 * @param SharedMount[] $mountpoints
145
-	 * @return mixed
146
-	 */
147
-	private function generateUniqueTarget($path, $view, array $mountpoints) {
148
-		$pathinfo = pathinfo($path);
149
-		$ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
150
-		$name = $pathinfo['filename'];
151
-		$dir = $pathinfo['dirname'];
152
-
153
-		$i = 2;
154
-		$absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
155
-		while ($view->file_exists($path) || isset($mountpoints[$absolutePath])) {
156
-			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
157
-			$absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
158
-			$i++;
159
-		}
160
-
161
-		return $path;
162
-	}
163
-
164
-	/**
165
-	 * Format a path to be relative to the /user/files/ directory
166
-	 *
167
-	 * @param string $path the absolute path
168
-	 * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
169
-	 * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
170
-	 */
171
-	protected function stripUserFilesPath($path) {
172
-		$trimmed = ltrim($path, '/');
173
-		$split = explode('/', $trimmed);
174
-
175
-		// it is not a file relative to data/user/files
176
-		if (count($split) < 3 || $split[1] !== 'files') {
177
-			\OC::$server->getLogger()->error('Can not strip userid and "files/" from path: ' . $path, ['app' => 'files_sharing']);
178
-			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
179
-		}
180
-
181
-		// skip 'user' and 'files'
182
-		$sliced = array_slice($split, 2);
183
-		$relPath = implode('/', $sliced);
184
-
185
-		return '/' . $relPath;
186
-	}
187
-
188
-	/**
189
-	 * Move the mount point to $target
190
-	 *
191
-	 * @param string $target the target mount point
192
-	 * @return bool
193
-	 */
194
-	public function moveMount($target) {
195
-		$relTargetPath = $this->stripUserFilesPath($target);
196
-		$share = $this->storage->getShare();
197
-
198
-		$result = true;
199
-
200
-		try {
201
-			$this->updateFileTarget($relTargetPath, $share);
202
-			$this->setMountPoint($target);
203
-			$this->storage->setMountPoint($relTargetPath);
204
-		} catch (\Exception $e) {
205
-			\OC::$server->getLogger()->logException($e, ['app' => 'files_sharing', 'message' => 'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"']);
206
-		}
207
-
208
-		return $result;
209
-	}
210
-
211
-	/**
212
-	 * Remove the mount points
213
-	 *
214
-	 * @return bool
215
-	 */
216
-	public function removeMount() {
217
-		$mountManager = \OC\Files\Filesystem::getMountManager();
218
-		/** @var \OCA\Files_Sharing\SharedStorage $storage */
219
-		$storage = $this->getStorage();
220
-		$result = $storage->unshareStorage();
221
-		$mountManager->removeMount($this->mountPoint);
222
-
223
-		return $result;
224
-	}
225
-
226
-	/**
227
-	 * @return \OCP\Share\IShare
228
-	 */
229
-	public function getShare() {
230
-		return $this->superShare;
231
-	}
232
-
233
-	/**
234
-	 * Get the file id of the root of the storage
235
-	 *
236
-	 * @return int
237
-	 */
238
-	public function getStorageRootId() {
239
-		return $this->getShare()->getNodeId();
240
-	}
241
-
242
-	/**
243
-	 * @return int
244
-	 */
245
-	public function getNumericStorageId() {
246
-		if (!is_null($this->getShare()->getNodeCacheEntry())) {
247
-			return $this->getShare()->getNodeCacheEntry()->getStorageId();
248
-		} else {
249
-			$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
250
-
251
-			$query = $builder->select('storage')
252
-				->from('filecache')
253
-				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
254
-
255
-			$result = $query->execute();
256
-			$row = $result->fetch();
257
-			$result->closeCursor();
258
-			if ($row) {
259
-				return (int)$row['storage'];
260
-			}
261
-			return -1;
262
-		}
263
-	}
264
-
265
-	public function getMountType() {
266
-		return 'shared';
267
-	}
44
+    /**
45
+     * @var \OCA\Files_Sharing\SharedStorage $storage
46
+     */
47
+    protected $storage = null;
48
+
49
+    /**
50
+     * @var \OC\Files\View
51
+     */
52
+    private $recipientView;
53
+
54
+    /**
55
+     * @var string
56
+     */
57
+    private $user;
58
+
59
+    /** @var \OCP\Share\IShare */
60
+    private $superShare;
61
+
62
+    /** @var \OCP\Share\IShare[] */
63
+    private $groupedShares;
64
+
65
+    /**
66
+     * @param string $storage
67
+     * @param SharedMount[] $mountpoints
68
+     * @param array $arguments
69
+     * @param IStorageFactory $loader
70
+     * @param View $recipientView
71
+     */
72
+    public function __construct($storage, array $mountpoints, $arguments, IStorageFactory $loader, View $recipientView, CappedMemoryCache $folderExistCache) {
73
+        $this->user = $arguments['user'];
74
+        $this->recipientView = $recipientView;
75
+
76
+        $this->superShare = $arguments['superShare'];
77
+        $this->groupedShares = $arguments['groupedShares'];
78
+
79
+        $newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints, $folderExistCache);
80
+        $absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
81
+        parent::__construct($storage, $absMountPoint, $arguments, $loader);
82
+    }
83
+
84
+    /**
85
+     * check if the parent folder exists otherwise move the mount point up
86
+     *
87
+     * @param \OCP\Share\IShare $share
88
+     * @param SharedMount[] $mountpoints
89
+     * @return string
90
+     */
91
+    private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints, CappedMemoryCache $folderExistCache) {
92
+        $mountPoint = basename($share->getTarget());
93
+        $parent = dirname($share->getTarget());
94
+
95
+        $event = new VerifyMountPointEvent($share, $this->recipientView, $parent);
96
+        /** @var IEventDispatcher $dispatcher */
97
+        $dispatcher = \OC::$server->query(IEventDispatcher::class);
98
+        $dispatcher->dispatchTyped($event);
99
+        $parent = $event->getParent();
100
+
101
+        if ($folderExistCache->hasKey($parent)) {
102
+            $parentExists = $folderExistCache->get($parent);
103
+        } else {
104
+            $parentExists = $this->recipientView->is_dir($parent);
105
+            $folderExistCache->set($parent, $parentExists);
106
+        }
107
+        if (!$parentExists) {
108
+            $parent = Helper::getShareFolder($this->recipientView);
109
+        }
110
+
111
+        $newMountPoint = $this->generateUniqueTarget(
112
+            \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
113
+            $this->recipientView,
114
+            $mountpoints
115
+        );
116
+
117
+        if ($newMountPoint !== $share->getTarget()) {
118
+            $this->updateFileTarget($newMountPoint, $share);
119
+        }
120
+
121
+        return $newMountPoint;
122
+    }
123
+
124
+    /**
125
+     * update fileTarget in the database if the mount point changed
126
+     *
127
+     * @param string $newPath
128
+     * @param \OCP\Share\IShare $share
129
+     * @return bool
130
+     */
131
+    private function updateFileTarget($newPath, &$share) {
132
+        $share->setTarget($newPath);
133
+
134
+        foreach ($this->groupedShares as $tmpShare) {
135
+            $tmpShare->setTarget($newPath);
136
+            \OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
137
+        }
138
+    }
139
+
140
+
141
+    /**
142
+     * @param string $path
143
+     * @param View $view
144
+     * @param SharedMount[] $mountpoints
145
+     * @return mixed
146
+     */
147
+    private function generateUniqueTarget($path, $view, array $mountpoints) {
148
+        $pathinfo = pathinfo($path);
149
+        $ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
150
+        $name = $pathinfo['filename'];
151
+        $dir = $pathinfo['dirname'];
152
+
153
+        $i = 2;
154
+        $absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
155
+        while ($view->file_exists($path) || isset($mountpoints[$absolutePath])) {
156
+            $path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
157
+            $absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
158
+            $i++;
159
+        }
160
+
161
+        return $path;
162
+    }
163
+
164
+    /**
165
+     * Format a path to be relative to the /user/files/ directory
166
+     *
167
+     * @param string $path the absolute path
168
+     * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
169
+     * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
170
+     */
171
+    protected function stripUserFilesPath($path) {
172
+        $trimmed = ltrim($path, '/');
173
+        $split = explode('/', $trimmed);
174
+
175
+        // it is not a file relative to data/user/files
176
+        if (count($split) < 3 || $split[1] !== 'files') {
177
+            \OC::$server->getLogger()->error('Can not strip userid and "files/" from path: ' . $path, ['app' => 'files_sharing']);
178
+            throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
179
+        }
180
+
181
+        // skip 'user' and 'files'
182
+        $sliced = array_slice($split, 2);
183
+        $relPath = implode('/', $sliced);
184
+
185
+        return '/' . $relPath;
186
+    }
187
+
188
+    /**
189
+     * Move the mount point to $target
190
+     *
191
+     * @param string $target the target mount point
192
+     * @return bool
193
+     */
194
+    public function moveMount($target) {
195
+        $relTargetPath = $this->stripUserFilesPath($target);
196
+        $share = $this->storage->getShare();
197
+
198
+        $result = true;
199
+
200
+        try {
201
+            $this->updateFileTarget($relTargetPath, $share);
202
+            $this->setMountPoint($target);
203
+            $this->storage->setMountPoint($relTargetPath);
204
+        } catch (\Exception $e) {
205
+            \OC::$server->getLogger()->logException($e, ['app' => 'files_sharing', 'message' => 'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"']);
206
+        }
207
+
208
+        return $result;
209
+    }
210
+
211
+    /**
212
+     * Remove the mount points
213
+     *
214
+     * @return bool
215
+     */
216
+    public function removeMount() {
217
+        $mountManager = \OC\Files\Filesystem::getMountManager();
218
+        /** @var \OCA\Files_Sharing\SharedStorage $storage */
219
+        $storage = $this->getStorage();
220
+        $result = $storage->unshareStorage();
221
+        $mountManager->removeMount($this->mountPoint);
222
+
223
+        return $result;
224
+    }
225
+
226
+    /**
227
+     * @return \OCP\Share\IShare
228
+     */
229
+    public function getShare() {
230
+        return $this->superShare;
231
+    }
232
+
233
+    /**
234
+     * Get the file id of the root of the storage
235
+     *
236
+     * @return int
237
+     */
238
+    public function getStorageRootId() {
239
+        return $this->getShare()->getNodeId();
240
+    }
241
+
242
+    /**
243
+     * @return int
244
+     */
245
+    public function getNumericStorageId() {
246
+        if (!is_null($this->getShare()->getNodeCacheEntry())) {
247
+            return $this->getShare()->getNodeCacheEntry()->getStorageId();
248
+        } else {
249
+            $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
250
+
251
+            $query = $builder->select('storage')
252
+                ->from('filecache')
253
+                ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
254
+
255
+            $result = $query->execute();
256
+            $row = $result->fetch();
257
+            $result->closeCursor();
258
+            if ($row) {
259
+                return (int)$row['storage'];
260
+            }
261
+            return -1;
262
+        }
263
+    }
264
+
265
+    public function getMountType() {
266
+        return 'shared';
267
+    }
268 268
 }
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Schedule/Plugin.php 2 patches
Indentation   +486 added lines, -486 removed lines patch added patch discarded remove patch
@@ -54,165 +54,165 @@  discard block
 block discarded – undo
54 54
 
55 55
 class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
56 56
 
57
-	/**
58
-	 * @var IConfig
59
-	 */
60
-	private $config;
61
-
62
-	/** @var ITip\Message[] */
63
-	private $schedulingResponses = [];
64
-
65
-	/** @var string|null */
66
-	private $pathOfCalendarObjectChange = null;
67
-
68
-	public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type';
69
-	public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL';
70
-
71
-	/**
72
-	 * @param IConfig $config
73
-	 */
74
-	public function __construct(IConfig $config) {
75
-		$this->config = $config;
76
-	}
77
-
78
-	/**
79
-	 * Initializes the plugin
80
-	 *
81
-	 * @param Server $server
82
-	 * @return void
83
-	 */
84
-	public function initialize(Server $server) {
85
-		parent::initialize($server);
86
-		$server->on('propFind', [$this, 'propFindDefaultCalendarUrl'], 90);
87
-		$server->on('afterWriteContent', [$this, 'dispatchSchedulingResponses']);
88
-		$server->on('afterCreateFile', [$this, 'dispatchSchedulingResponses']);
89
-	}
90
-
91
-	/**
92
-	 * This method handler is invoked during fetching of properties.
93
-	 *
94
-	 * We use this event to add calendar-auto-schedule-specific properties.
95
-	 *
96
-	 * @param PropFind $propFind
97
-	 * @param INode $node
98
-	 * @return void
99
-	 */
100
-	public function propFind(PropFind $propFind, INode $node) {
101
-		if ($node instanceof IPrincipal) {
102
-			// overwrite Sabre/Dav's implementation
103
-			$propFind->handle(self::CALENDAR_USER_TYPE, function () use ($node) {
104
-				if ($node instanceof IProperties) {
105
-					$props = $node->getProperties([self::CALENDAR_USER_TYPE]);
106
-
107
-					if (isset($props[self::CALENDAR_USER_TYPE])) {
108
-						return $props[self::CALENDAR_USER_TYPE];
109
-					}
110
-				}
111
-
112
-				return 'INDIVIDUAL';
113
-			});
114
-		}
115
-
116
-		parent::propFind($propFind, $node);
117
-	}
118
-
119
-	/**
120
-	 * Returns a list of addresses that are associated with a principal.
121
-	 *
122
-	 * @param string $principal
123
-	 * @return array
124
-	 */
125
-	protected function getAddressesForPrincipal($principal) {
126
-		$result = parent::getAddressesForPrincipal($principal);
127
-
128
-		if ($result === null) {
129
-			$result = [];
130
-		}
131
-
132
-		return $result;
133
-	}
134
-
135
-	/**
136
-	 * @param RequestInterface $request
137
-	 * @param ResponseInterface $response
138
-	 * @param VCalendar $vCal
139
-	 * @param mixed $calendarPath
140
-	 * @param mixed $modified
141
-	 * @param mixed $isNew
142
-	 */
143
-	public function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) {
144
-		// Save the first path we get as a calendar-object-change request
145
-		if (!$this->pathOfCalendarObjectChange) {
146
-			$this->pathOfCalendarObjectChange = $request->getPath();
147
-		}
148
-
149
-		parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew);
150
-	}
151
-
152
-	/**
153
-	 * @inheritDoc
154
-	 */
155
-	public function scheduleLocalDelivery(ITip\Message $iTipMessage):void {
156
-		parent::scheduleLocalDelivery($iTipMessage);
157
-
158
-		// We only care when the message was successfully delivered locally
159
-		if ($iTipMessage->scheduleStatus !== '1.2;Message delivered locally') {
160
-			return;
161
-		}
162
-
163
-		// We only care about request. reply and cancel are properly handled
164
-		// by parent::scheduleLocalDelivery already
165
-		if (strcasecmp($iTipMessage->method, 'REQUEST') !== 0) {
166
-			return;
167
-		}
168
-
169
-		// If parent::scheduleLocalDelivery set scheduleStatus to 1.2,
170
-		// it means that it was successfully delivered locally.
171
-		// Meaning that the ACL plugin is loaded and that a principial
172
-		// exists for the given recipient id, no need to double check
173
-		/** @var \Sabre\DAVACL\Plugin $aclPlugin */
174
-		$aclPlugin = $this->server->getPlugin('acl');
175
-		$principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient);
176
-		$calendarUserType = $this->getCalendarUserTypeForPrincipal($principalUri);
177
-		if (strcasecmp($calendarUserType, 'ROOM') !== 0 && strcasecmp($calendarUserType, 'RESOURCE') !== 0) {
178
-			return;
179
-		}
180
-
181
-		$attendee = $this->getCurrentAttendee($iTipMessage);
182
-		if (!$attendee) {
183
-			return;
184
-		}
185
-
186
-		// We only respond when a response was actually requested
187
-		$rsvp = $this->getAttendeeRSVP($attendee);
188
-		if (!$rsvp) {
189
-			return;
190
-		}
191
-
192
-		if (!isset($iTipMessage->message)) {
193
-			return;
194
-		}
195
-
196
-		$vcalendar = $iTipMessage->message;
197
-		if (!isset($vcalendar->VEVENT)) {
198
-			return;
199
-		}
200
-
201
-		/** @var Component $vevent */
202
-		$vevent = $vcalendar->VEVENT;
203
-
204
-		// We don't support autoresponses for recurrencing events for now
205
-		if (isset($vevent->RRULE) || isset($vevent->RDATE)) {
206
-			return;
207
-		}
208
-
209
-		$dtstart = $vevent->DTSTART;
210
-		$dtend =  $this->getDTEndFromVEvent($vevent);
211
-		$uid = $vevent->UID->getValue();
212
-		$sequence = isset($vevent->SEQUENCE) ? $vevent->SEQUENCE->getValue() : 0;
213
-		$recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->serialize() : '';
214
-
215
-		$message = <<<EOF
57
+    /**
58
+     * @var IConfig
59
+     */
60
+    private $config;
61
+
62
+    /** @var ITip\Message[] */
63
+    private $schedulingResponses = [];
64
+
65
+    /** @var string|null */
66
+    private $pathOfCalendarObjectChange = null;
67
+
68
+    public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type';
69
+    public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL';
70
+
71
+    /**
72
+     * @param IConfig $config
73
+     */
74
+    public function __construct(IConfig $config) {
75
+        $this->config = $config;
76
+    }
77
+
78
+    /**
79
+     * Initializes the plugin
80
+     *
81
+     * @param Server $server
82
+     * @return void
83
+     */
84
+    public function initialize(Server $server) {
85
+        parent::initialize($server);
86
+        $server->on('propFind', [$this, 'propFindDefaultCalendarUrl'], 90);
87
+        $server->on('afterWriteContent', [$this, 'dispatchSchedulingResponses']);
88
+        $server->on('afterCreateFile', [$this, 'dispatchSchedulingResponses']);
89
+    }
90
+
91
+    /**
92
+     * This method handler is invoked during fetching of properties.
93
+     *
94
+     * We use this event to add calendar-auto-schedule-specific properties.
95
+     *
96
+     * @param PropFind $propFind
97
+     * @param INode $node
98
+     * @return void
99
+     */
100
+    public function propFind(PropFind $propFind, INode $node) {
101
+        if ($node instanceof IPrincipal) {
102
+            // overwrite Sabre/Dav's implementation
103
+            $propFind->handle(self::CALENDAR_USER_TYPE, function () use ($node) {
104
+                if ($node instanceof IProperties) {
105
+                    $props = $node->getProperties([self::CALENDAR_USER_TYPE]);
106
+
107
+                    if (isset($props[self::CALENDAR_USER_TYPE])) {
108
+                        return $props[self::CALENDAR_USER_TYPE];
109
+                    }
110
+                }
111
+
112
+                return 'INDIVIDUAL';
113
+            });
114
+        }
115
+
116
+        parent::propFind($propFind, $node);
117
+    }
118
+
119
+    /**
120
+     * Returns a list of addresses that are associated with a principal.
121
+     *
122
+     * @param string $principal
123
+     * @return array
124
+     */
125
+    protected function getAddressesForPrincipal($principal) {
126
+        $result = parent::getAddressesForPrincipal($principal);
127
+
128
+        if ($result === null) {
129
+            $result = [];
130
+        }
131
+
132
+        return $result;
133
+    }
134
+
135
+    /**
136
+     * @param RequestInterface $request
137
+     * @param ResponseInterface $response
138
+     * @param VCalendar $vCal
139
+     * @param mixed $calendarPath
140
+     * @param mixed $modified
141
+     * @param mixed $isNew
142
+     */
143
+    public function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) {
144
+        // Save the first path we get as a calendar-object-change request
145
+        if (!$this->pathOfCalendarObjectChange) {
146
+            $this->pathOfCalendarObjectChange = $request->getPath();
147
+        }
148
+
149
+        parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew);
150
+    }
151
+
152
+    /**
153
+     * @inheritDoc
154
+     */
155
+    public function scheduleLocalDelivery(ITip\Message $iTipMessage):void {
156
+        parent::scheduleLocalDelivery($iTipMessage);
157
+
158
+        // We only care when the message was successfully delivered locally
159
+        if ($iTipMessage->scheduleStatus !== '1.2;Message delivered locally') {
160
+            return;
161
+        }
162
+
163
+        // We only care about request. reply and cancel are properly handled
164
+        // by parent::scheduleLocalDelivery already
165
+        if (strcasecmp($iTipMessage->method, 'REQUEST') !== 0) {
166
+            return;
167
+        }
168
+
169
+        // If parent::scheduleLocalDelivery set scheduleStatus to 1.2,
170
+        // it means that it was successfully delivered locally.
171
+        // Meaning that the ACL plugin is loaded and that a principial
172
+        // exists for the given recipient id, no need to double check
173
+        /** @var \Sabre\DAVACL\Plugin $aclPlugin */
174
+        $aclPlugin = $this->server->getPlugin('acl');
175
+        $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient);
176
+        $calendarUserType = $this->getCalendarUserTypeForPrincipal($principalUri);
177
+        if (strcasecmp($calendarUserType, 'ROOM') !== 0 && strcasecmp($calendarUserType, 'RESOURCE') !== 0) {
178
+            return;
179
+        }
180
+
181
+        $attendee = $this->getCurrentAttendee($iTipMessage);
182
+        if (!$attendee) {
183
+            return;
184
+        }
185
+
186
+        // We only respond when a response was actually requested
187
+        $rsvp = $this->getAttendeeRSVP($attendee);
188
+        if (!$rsvp) {
189
+            return;
190
+        }
191
+
192
+        if (!isset($iTipMessage->message)) {
193
+            return;
194
+        }
195
+
196
+        $vcalendar = $iTipMessage->message;
197
+        if (!isset($vcalendar->VEVENT)) {
198
+            return;
199
+        }
200
+
201
+        /** @var Component $vevent */
202
+        $vevent = $vcalendar->VEVENT;
203
+
204
+        // We don't support autoresponses for recurrencing events for now
205
+        if (isset($vevent->RRULE) || isset($vevent->RDATE)) {
206
+            return;
207
+        }
208
+
209
+        $dtstart = $vevent->DTSTART;
210
+        $dtend =  $this->getDTEndFromVEvent($vevent);
211
+        $uid = $vevent->UID->getValue();
212
+        $sequence = isset($vevent->SEQUENCE) ? $vevent->SEQUENCE->getValue() : 0;
213
+        $recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->serialize() : '';
214
+
215
+        $message = <<<EOF
216 216
 BEGIN:VCALENDAR
217 217
 PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
218 218
 METHOD:REPLY
@@ -227,331 +227,331 @@  discard block
 block discarded – undo
227 227
 END:VCALENDAR
228 228
 EOF;
229 229
 
230
-		if ($this->isAvailableAtTime($attendee->getValue(), $dtstart->getDateTime(), $dtend->getDateTime(), $uid)) {
231
-			$partStat = 'ACCEPTED';
232
-		} else {
233
-			$partStat = 'DECLINED';
234
-		}
235
-
236
-		$vObject = Reader::read(vsprintf($message, [
237
-			$partStat,
238
-			$iTipMessage->recipient,
239
-			$iTipMessage->sender,
240
-			$uid,
241
-			$sequence,
242
-			$recurrenceId
243
-		]));
244
-
245
-		$responseITipMessage = new ITip\Message();
246
-		$responseITipMessage->uid = $uid;
247
-		$responseITipMessage->component = 'VEVENT';
248
-		$responseITipMessage->method = 'REPLY';
249
-		$responseITipMessage->sequence = $sequence;
250
-		$responseITipMessage->sender = $iTipMessage->recipient;
251
-		$responseITipMessage->recipient = $iTipMessage->sender;
252
-		$responseITipMessage->message = $vObject;
253
-
254
-		// We can't dispatch them now already, because the organizers calendar-object
255
-		// was not yet created. Hence Sabre/DAV won't find a calendar-object, when we
256
-		// send our reply.
257
-		$this->schedulingResponses[] = $responseITipMessage;
258
-	}
259
-
260
-	/**
261
-	 * @param string $uri
262
-	 */
263
-	public function dispatchSchedulingResponses(string $uri):void {
264
-		if ($uri !== $this->pathOfCalendarObjectChange) {
265
-			return;
266
-		}
267
-
268
-		foreach ($this->schedulingResponses as $schedulingResponse) {
269
-			$this->scheduleLocalDelivery($schedulingResponse);
270
-		}
271
-	}
272
-
273
-	/**
274
-	 * Always use the personal calendar as target for scheduled events
275
-	 *
276
-	 * @param PropFind $propFind
277
-	 * @param INode $node
278
-	 * @return void
279
-	 */
280
-	public function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) {
281
-		if ($node instanceof IPrincipal) {
282
-			$propFind->handle(self::SCHEDULE_DEFAULT_CALENDAR_URL, function () use ($node) {
283
-				/** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
284
-				$caldavPlugin = $this->server->getPlugin('caldav');
285
-				$principalUrl = $node->getPrincipalUrl();
286
-
287
-				$calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
288
-				if (!$calendarHomePath) {
289
-					return null;
290
-				}
291
-
292
-				if (strpos($principalUrl, 'principals/users') === 0) {
293
-					list(, $userId) = split($principalUrl);
294
-					$uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
295
-					$displayName = CalDavBackend::PERSONAL_CALENDAR_NAME;
296
-				} elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
297
-						  strpos($principalUrl, 'principals/calendar-rooms') === 0) {
298
-					$uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
299
-					$displayName = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
300
-				} else {
301
-					// How did we end up here?
302
-					// TODO - throw exception or just ignore?
303
-					return null;
304
-				}
305
-
306
-				/** @var CalendarHome $calendarHome */
307
-				$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
308
-				if (!$calendarHome->childExists($uri)) {
309
-					$calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
310
-						'{DAV:}displayname' => $displayName,
311
-					]);
312
-				}
313
-
314
-				$result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
315
-				if (empty($result)) {
316
-					return null;
317
-				}
318
-
319
-				return new LocalHref($result[0]['href']);
320
-			});
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * Returns a list of addresses that are associated with a principal.
326
-	 *
327
-	 * @param string $principal
328
-	 * @return string|null
329
-	 */
330
-	protected function getCalendarUserTypeForPrincipal($principal):?string {
331
-		$calendarUserType = '{' . self::NS_CALDAV . '}calendar-user-type';
332
-		$properties = $this->server->getProperties(
333
-			$principal,
334
-			[$calendarUserType]
335
-		);
336
-
337
-		// If we can't find this information, we'll stop processing
338
-		if (!isset($properties[$calendarUserType])) {
339
-			return null;
340
-		}
341
-
342
-		return $properties[$calendarUserType];
343
-	}
344
-
345
-	/**
346
-	 * @param ITip\Message $iTipMessage
347
-	 * @return null|Property
348
-	 */
349
-	private function getCurrentAttendee(ITip\Message $iTipMessage):?Property {
350
-		/** @var VEvent $vevent */
351
-		$vevent = $iTipMessage->message->VEVENT;
352
-		$attendees = $vevent->select('ATTENDEE');
353
-		foreach ($attendees as $attendee) {
354
-			/** @var Property $attendee */
355
-			if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
356
-				return $attendee;
357
-			}
358
-		}
359
-		return null;
360
-	}
361
-
362
-	/**
363
-	 * @param Property|null $attendee
364
-	 * @return bool
365
-	 */
366
-	private function getAttendeeRSVP(Property $attendee = null):bool {
367
-		if ($attendee !== null) {
368
-			$rsvp = $attendee->offsetGet('RSVP');
369
-			if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
370
-				return true;
371
-			}
372
-		}
373
-		// RFC 5545 3.2.17: default RSVP is false
374
-		return false;
375
-	}
376
-
377
-	/**
378
-	 * @param VEvent $vevent
379
-	 * @return Property\ICalendar\DateTime
380
-	 */
381
-	private function getDTEndFromVEvent(VEvent $vevent):Property\ICalendar\DateTime {
382
-		if (isset($vevent->DTEND)) {
383
-			return $vevent->DTEND;
384
-		}
385
-
386
-		if (isset($vevent->DURATION)) {
387
-			$isFloating = $vevent->DTSTART->isFloating();
388
-			/** @var Property\ICalendar\DateTime $end */
389
-			$end = clone $vevent->DTSTART;
390
-			$endDateTime = $end->getDateTime();
391
-			$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
392
-			$end->setDateTime($endDateTime, $isFloating);
393
-			return $end;
394
-		}
395
-
396
-		if (!$vevent->DTSTART->hasTime()) {
397
-			$isFloating = $vevent->DTSTART->isFloating();
398
-			/** @var Property\ICalendar\DateTime $end */
399
-			$end = clone $vevent->DTSTART;
400
-			$endDateTime = $end->getDateTime();
401
-			$endDateTime = $endDateTime->modify('+1 day');
402
-			$end->setDateTime($endDateTime, $isFloating);
403
-			return $end;
404
-		}
405
-
406
-		return clone $vevent->DTSTART;
407
-	}
408
-
409
-	/**
410
-	 * @param string $email
411
-	 * @param \DateTimeInterface $start
412
-	 * @param \DateTimeInterface $end
413
-	 * @param string $ignoreUID
414
-	 * @return bool
415
-	 */
416
-	private function isAvailableAtTime(string $email, \DateTimeInterface $start, \DateTimeInterface $end, string $ignoreUID):bool {
417
-		// This method is heavily inspired by Sabre\CalDAV\Schedule\Plugin::scheduleLocalDelivery
418
-		// and Sabre\CalDAV\Schedule\Plugin::getFreeBusyForEmail
419
-
420
-		$aclPlugin = $this->server->getPlugin('acl');
421
-		$this->server->removeListener('propFind', [$aclPlugin, 'propFind']);
422
-
423
-		$result = $aclPlugin->principalSearch(
424
-			['{http://sabredav.org/ns}email-address' => $this->stripOffMailTo($email)],
425
-			[
426
-				'{DAV:}principal-URL',
427
-				'{' . self::NS_CALDAV . '}calendar-home-set',
428
-				'{' . self::NS_CALDAV . '}schedule-inbox-URL',
429
-				'{http://sabredav.org/ns}email-address',
430
-
431
-			]
432
-		);
433
-		$this->server->on('propFind', [$aclPlugin, 'propFind'], 20);
434
-
435
-
436
-		// Grabbing the calendar list
437
-		$objects = [];
438
-		$calendarTimeZone = new DateTimeZone('UTC');
439
-
440
-		$homePath = $result[0][200]['{' . self::NS_CALDAV . '}calendar-home-set']->getHref();
441
-		foreach ($this->server->tree->getNodeForPath($homePath)->getChildren() as $node) {
442
-			if (!$node instanceof ICalendar) {
443
-				continue;
444
-			}
445
-
446
-			// Getting the list of object uris within the time-range
447
-			$urls = $node->calendarQuery([
448
-				'name' => 'VCALENDAR',
449
-				'comp-filters' => [
450
-					[
451
-						'name' => 'VEVENT',
452
-						'is-not-defined' => false,
453
-						'time-range' => [
454
-							'start' => $start,
455
-							'end' => $end,
456
-						],
457
-						'comp-filters'   => [],
458
-						'prop-filters'   => [],
459
-					],
460
-					[
461
-						'name' => 'VEVENT',
462
-						'is-not-defined' => false,
463
-						'time-range' => null,
464
-						'comp-filters'   => [],
465
-						'prop-filters'   => [
466
-							[
467
-								'name'           => 'UID',
468
-								'is-not-defined' => false,
469
-								'time-range'     => null,
470
-								'text-match'     => [
471
-									'value' => $ignoreUID,
472
-									'negate-condition' => true,
473
-									'collation' => 'i;octet',
474
-								],
475
-								'param-filters' => [],
476
-							],
477
-						]
478
-					],
479
-				],
480
-				'prop-filters' => [],
481
-				'is-not-defined' => false,
482
-				'time-range' => null,
483
-			]);
484
-
485
-			foreach ($urls as $url) {
486
-				$objects[] = $node->getChild($url)->get();
487
-			}
488
-		}
489
-
490
-		$inboxProps = $this->server->getProperties(
491
-			$result[0][200]['{' . self::NS_CALDAV . '}schedule-inbox-URL']->getHref(),
492
-			['{' . self::NS_CALDAV . '}calendar-availability']
493
-		);
494
-
495
-		$vcalendar = new VCalendar();
496
-		$vcalendar->METHOD = 'REPLY';
497
-
498
-		$generator = new FreeBusyGenerator();
499
-		$generator->setObjects($objects);
500
-		$generator->setTimeRange($start, $end);
501
-		$generator->setBaseObject($vcalendar);
502
-		$generator->setTimeZone($calendarTimeZone);
503
-
504
-		if (isset($inboxProps['{' . self::NS_CALDAV . '}calendar-availability'])) {
505
-			$generator->setVAvailability(
506
-				Reader::read(
507
-					$inboxProps['{' . self::NS_CALDAV . '}calendar-availability']
508
-				)
509
-			);
510
-		}
511
-
512
-		$result = $generator->getResult();
513
-		if (!isset($result->VFREEBUSY)) {
514
-			return false;
515
-		}
516
-
517
-		/** @var Component $freeBusyComponent */
518
-		$freeBusyComponent = $result->VFREEBUSY;
519
-		$freeBusyProperties = $freeBusyComponent->select('FREEBUSY');
520
-		// If there is no Free-busy property at all, the time-range is empty and available
521
-		if (count($freeBusyProperties) === 0) {
522
-			return true;
523
-		}
524
-
525
-		// If more than one Free-Busy property was returned, it means that an event
526
-		// starts or ends inside this time-range, so it's not availabe and we return false
527
-		if (count($freeBusyProperties) > 1) {
528
-			return false;
529
-		}
530
-
531
-		/** @var Property $freeBusyProperty */
532
-		$freeBusyProperty = $freeBusyProperties[0];
533
-		if (!$freeBusyProperty->offsetExists('FBTYPE')) {
534
-			// If there is no FBTYPE, it means it's busy
535
-			return false;
536
-		}
537
-
538
-		$fbTypeParameter = $freeBusyProperty->offsetGet('FBTYPE');
539
-		if (!($fbTypeParameter instanceof Parameter)) {
540
-			return false;
541
-		}
542
-
543
-		return (strcasecmp($fbTypeParameter->getValue(), 'FREE') === 0);
544
-	}
545
-
546
-	/**
547
-	 * @param string $email
548
-	 * @return string
549
-	 */
550
-	private function stripOffMailTo(string $email): string {
551
-		if (stripos($email, 'mailto:')  === 0) {
552
-			return substr($email, 7);
553
-		}
554
-
555
-		return $email;
556
-	}
230
+        if ($this->isAvailableAtTime($attendee->getValue(), $dtstart->getDateTime(), $dtend->getDateTime(), $uid)) {
231
+            $partStat = 'ACCEPTED';
232
+        } else {
233
+            $partStat = 'DECLINED';
234
+        }
235
+
236
+        $vObject = Reader::read(vsprintf($message, [
237
+            $partStat,
238
+            $iTipMessage->recipient,
239
+            $iTipMessage->sender,
240
+            $uid,
241
+            $sequence,
242
+            $recurrenceId
243
+        ]));
244
+
245
+        $responseITipMessage = new ITip\Message();
246
+        $responseITipMessage->uid = $uid;
247
+        $responseITipMessage->component = 'VEVENT';
248
+        $responseITipMessage->method = 'REPLY';
249
+        $responseITipMessage->sequence = $sequence;
250
+        $responseITipMessage->sender = $iTipMessage->recipient;
251
+        $responseITipMessage->recipient = $iTipMessage->sender;
252
+        $responseITipMessage->message = $vObject;
253
+
254
+        // We can't dispatch them now already, because the organizers calendar-object
255
+        // was not yet created. Hence Sabre/DAV won't find a calendar-object, when we
256
+        // send our reply.
257
+        $this->schedulingResponses[] = $responseITipMessage;
258
+    }
259
+
260
+    /**
261
+     * @param string $uri
262
+     */
263
+    public function dispatchSchedulingResponses(string $uri):void {
264
+        if ($uri !== $this->pathOfCalendarObjectChange) {
265
+            return;
266
+        }
267
+
268
+        foreach ($this->schedulingResponses as $schedulingResponse) {
269
+            $this->scheduleLocalDelivery($schedulingResponse);
270
+        }
271
+    }
272
+
273
+    /**
274
+     * Always use the personal calendar as target for scheduled events
275
+     *
276
+     * @param PropFind $propFind
277
+     * @param INode $node
278
+     * @return void
279
+     */
280
+    public function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) {
281
+        if ($node instanceof IPrincipal) {
282
+            $propFind->handle(self::SCHEDULE_DEFAULT_CALENDAR_URL, function () use ($node) {
283
+                /** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
284
+                $caldavPlugin = $this->server->getPlugin('caldav');
285
+                $principalUrl = $node->getPrincipalUrl();
286
+
287
+                $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
288
+                if (!$calendarHomePath) {
289
+                    return null;
290
+                }
291
+
292
+                if (strpos($principalUrl, 'principals/users') === 0) {
293
+                    list(, $userId) = split($principalUrl);
294
+                    $uri = $this->config->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
295
+                    $displayName = CalDavBackend::PERSONAL_CALENDAR_NAME;
296
+                } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
297
+                          strpos($principalUrl, 'principals/calendar-rooms') === 0) {
298
+                    $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
299
+                    $displayName = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
300
+                } else {
301
+                    // How did we end up here?
302
+                    // TODO - throw exception or just ignore?
303
+                    return null;
304
+                }
305
+
306
+                /** @var CalendarHome $calendarHome */
307
+                $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
308
+                if (!$calendarHome->childExists($uri)) {
309
+                    $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
310
+                        '{DAV:}displayname' => $displayName,
311
+                    ]);
312
+                }
313
+
314
+                $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
315
+                if (empty($result)) {
316
+                    return null;
317
+                }
318
+
319
+                return new LocalHref($result[0]['href']);
320
+            });
321
+        }
322
+    }
323
+
324
+    /**
325
+     * Returns a list of addresses that are associated with a principal.
326
+     *
327
+     * @param string $principal
328
+     * @return string|null
329
+     */
330
+    protected function getCalendarUserTypeForPrincipal($principal):?string {
331
+        $calendarUserType = '{' . self::NS_CALDAV . '}calendar-user-type';
332
+        $properties = $this->server->getProperties(
333
+            $principal,
334
+            [$calendarUserType]
335
+        );
336
+
337
+        // If we can't find this information, we'll stop processing
338
+        if (!isset($properties[$calendarUserType])) {
339
+            return null;
340
+        }
341
+
342
+        return $properties[$calendarUserType];
343
+    }
344
+
345
+    /**
346
+     * @param ITip\Message $iTipMessage
347
+     * @return null|Property
348
+     */
349
+    private function getCurrentAttendee(ITip\Message $iTipMessage):?Property {
350
+        /** @var VEvent $vevent */
351
+        $vevent = $iTipMessage->message->VEVENT;
352
+        $attendees = $vevent->select('ATTENDEE');
353
+        foreach ($attendees as $attendee) {
354
+            /** @var Property $attendee */
355
+            if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
356
+                return $attendee;
357
+            }
358
+        }
359
+        return null;
360
+    }
361
+
362
+    /**
363
+     * @param Property|null $attendee
364
+     * @return bool
365
+     */
366
+    private function getAttendeeRSVP(Property $attendee = null):bool {
367
+        if ($attendee !== null) {
368
+            $rsvp = $attendee->offsetGet('RSVP');
369
+            if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
370
+                return true;
371
+            }
372
+        }
373
+        // RFC 5545 3.2.17: default RSVP is false
374
+        return false;
375
+    }
376
+
377
+    /**
378
+     * @param VEvent $vevent
379
+     * @return Property\ICalendar\DateTime
380
+     */
381
+    private function getDTEndFromVEvent(VEvent $vevent):Property\ICalendar\DateTime {
382
+        if (isset($vevent->DTEND)) {
383
+            return $vevent->DTEND;
384
+        }
385
+
386
+        if (isset($vevent->DURATION)) {
387
+            $isFloating = $vevent->DTSTART->isFloating();
388
+            /** @var Property\ICalendar\DateTime $end */
389
+            $end = clone $vevent->DTSTART;
390
+            $endDateTime = $end->getDateTime();
391
+            $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
392
+            $end->setDateTime($endDateTime, $isFloating);
393
+            return $end;
394
+        }
395
+
396
+        if (!$vevent->DTSTART->hasTime()) {
397
+            $isFloating = $vevent->DTSTART->isFloating();
398
+            /** @var Property\ICalendar\DateTime $end */
399
+            $end = clone $vevent->DTSTART;
400
+            $endDateTime = $end->getDateTime();
401
+            $endDateTime = $endDateTime->modify('+1 day');
402
+            $end->setDateTime($endDateTime, $isFloating);
403
+            return $end;
404
+        }
405
+
406
+        return clone $vevent->DTSTART;
407
+    }
408
+
409
+    /**
410
+     * @param string $email
411
+     * @param \DateTimeInterface $start
412
+     * @param \DateTimeInterface $end
413
+     * @param string $ignoreUID
414
+     * @return bool
415
+     */
416
+    private function isAvailableAtTime(string $email, \DateTimeInterface $start, \DateTimeInterface $end, string $ignoreUID):bool {
417
+        // This method is heavily inspired by Sabre\CalDAV\Schedule\Plugin::scheduleLocalDelivery
418
+        // and Sabre\CalDAV\Schedule\Plugin::getFreeBusyForEmail
419
+
420
+        $aclPlugin = $this->server->getPlugin('acl');
421
+        $this->server->removeListener('propFind', [$aclPlugin, 'propFind']);
422
+
423
+        $result = $aclPlugin->principalSearch(
424
+            ['{http://sabredav.org/ns}email-address' => $this->stripOffMailTo($email)],
425
+            [
426
+                '{DAV:}principal-URL',
427
+                '{' . self::NS_CALDAV . '}calendar-home-set',
428
+                '{' . self::NS_CALDAV . '}schedule-inbox-URL',
429
+                '{http://sabredav.org/ns}email-address',
430
+
431
+            ]
432
+        );
433
+        $this->server->on('propFind', [$aclPlugin, 'propFind'], 20);
434
+
435
+
436
+        // Grabbing the calendar list
437
+        $objects = [];
438
+        $calendarTimeZone = new DateTimeZone('UTC');
439
+
440
+        $homePath = $result[0][200]['{' . self::NS_CALDAV . '}calendar-home-set']->getHref();
441
+        foreach ($this->server->tree->getNodeForPath($homePath)->getChildren() as $node) {
442
+            if (!$node instanceof ICalendar) {
443
+                continue;
444
+            }
445
+
446
+            // Getting the list of object uris within the time-range
447
+            $urls = $node->calendarQuery([
448
+                'name' => 'VCALENDAR',
449
+                'comp-filters' => [
450
+                    [
451
+                        'name' => 'VEVENT',
452
+                        'is-not-defined' => false,
453
+                        'time-range' => [
454
+                            'start' => $start,
455
+                            'end' => $end,
456
+                        ],
457
+                        'comp-filters'   => [],
458
+                        'prop-filters'   => [],
459
+                    ],
460
+                    [
461
+                        'name' => 'VEVENT',
462
+                        'is-not-defined' => false,
463
+                        'time-range' => null,
464
+                        'comp-filters'   => [],
465
+                        'prop-filters'   => [
466
+                            [
467
+                                'name'           => 'UID',
468
+                                'is-not-defined' => false,
469
+                                'time-range'     => null,
470
+                                'text-match'     => [
471
+                                    'value' => $ignoreUID,
472
+                                    'negate-condition' => true,
473
+                                    'collation' => 'i;octet',
474
+                                ],
475
+                                'param-filters' => [],
476
+                            ],
477
+                        ]
478
+                    ],
479
+                ],
480
+                'prop-filters' => [],
481
+                'is-not-defined' => false,
482
+                'time-range' => null,
483
+            ]);
484
+
485
+            foreach ($urls as $url) {
486
+                $objects[] = $node->getChild($url)->get();
487
+            }
488
+        }
489
+
490
+        $inboxProps = $this->server->getProperties(
491
+            $result[0][200]['{' . self::NS_CALDAV . '}schedule-inbox-URL']->getHref(),
492
+            ['{' . self::NS_CALDAV . '}calendar-availability']
493
+        );
494
+
495
+        $vcalendar = new VCalendar();
496
+        $vcalendar->METHOD = 'REPLY';
497
+
498
+        $generator = new FreeBusyGenerator();
499
+        $generator->setObjects($objects);
500
+        $generator->setTimeRange($start, $end);
501
+        $generator->setBaseObject($vcalendar);
502
+        $generator->setTimeZone($calendarTimeZone);
503
+
504
+        if (isset($inboxProps['{' . self::NS_CALDAV . '}calendar-availability'])) {
505
+            $generator->setVAvailability(
506
+                Reader::read(
507
+                    $inboxProps['{' . self::NS_CALDAV . '}calendar-availability']
508
+                )
509
+            );
510
+        }
511
+
512
+        $result = $generator->getResult();
513
+        if (!isset($result->VFREEBUSY)) {
514
+            return false;
515
+        }
516
+
517
+        /** @var Component $freeBusyComponent */
518
+        $freeBusyComponent = $result->VFREEBUSY;
519
+        $freeBusyProperties = $freeBusyComponent->select('FREEBUSY');
520
+        // If there is no Free-busy property at all, the time-range is empty and available
521
+        if (count($freeBusyProperties) === 0) {
522
+            return true;
523
+        }
524
+
525
+        // If more than one Free-Busy property was returned, it means that an event
526
+        // starts or ends inside this time-range, so it's not availabe and we return false
527
+        if (count($freeBusyProperties) > 1) {
528
+            return false;
529
+        }
530
+
531
+        /** @var Property $freeBusyProperty */
532
+        $freeBusyProperty = $freeBusyProperties[0];
533
+        if (!$freeBusyProperty->offsetExists('FBTYPE')) {
534
+            // If there is no FBTYPE, it means it's busy
535
+            return false;
536
+        }
537
+
538
+        $fbTypeParameter = $freeBusyProperty->offsetGet('FBTYPE');
539
+        if (!($fbTypeParameter instanceof Parameter)) {
540
+            return false;
541
+        }
542
+
543
+        return (strcasecmp($fbTypeParameter->getValue(), 'FREE') === 0);
544
+    }
545
+
546
+    /**
547
+     * @param string $email
548
+     * @return string
549
+     */
550
+    private function stripOffMailTo(string $email): string {
551
+        if (stripos($email, 'mailto:')  === 0) {
552
+            return substr($email, 7);
553
+        }
554
+
555
+        return $email;
556
+    }
557 557
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -65,8 +65,8 @@  discard block
 block discarded – undo
65 65
 	/** @var string|null */
66 66
 	private $pathOfCalendarObjectChange = null;
67 67
 
68
-	public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type';
69
-	public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL';
68
+	public const CALENDAR_USER_TYPE = '{'.self::NS_CALDAV.'}calendar-user-type';
69
+	public const SCHEDULE_DEFAULT_CALENDAR_URL = '{'.Plugin::NS_CALDAV.'}schedule-default-calendar-URL';
70 70
 
71 71
 	/**
72 72
 	 * @param IConfig $config
@@ -100,7 +100,7 @@  discard block
 block discarded – undo
100 100
 	public function propFind(PropFind $propFind, INode $node) {
101 101
 		if ($node instanceof IPrincipal) {
102 102
 			// overwrite Sabre/Dav's implementation
103
-			$propFind->handle(self::CALENDAR_USER_TYPE, function () use ($node) {
103
+			$propFind->handle(self::CALENDAR_USER_TYPE, function() use ($node) {
104 104
 				if ($node instanceof IProperties) {
105 105
 					$props = $node->getProperties([self::CALENDAR_USER_TYPE]);
106 106
 
@@ -207,7 +207,7 @@  discard block
 block discarded – undo
207 207
 		}
208 208
 
209 209
 		$dtstart = $vevent->DTSTART;
210
-		$dtend =  $this->getDTEndFromVEvent($vevent);
210
+		$dtend = $this->getDTEndFromVEvent($vevent);
211 211
 		$uid = $vevent->UID->getValue();
212 212
 		$sequence = isset($vevent->SEQUENCE) ? $vevent->SEQUENCE->getValue() : 0;
213 213
 		$recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->serialize() : '';
@@ -279,7 +279,7 @@  discard block
 block discarded – undo
279 279
 	 */
280 280
 	public function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) {
281 281
 		if ($node instanceof IPrincipal) {
282
-			$propFind->handle(self::SCHEDULE_DEFAULT_CALENDAR_URL, function () use ($node) {
282
+			$propFind->handle(self::SCHEDULE_DEFAULT_CALENDAR_URL, function() use ($node) {
283 283
 				/** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
284 284
 				$caldavPlugin = $this->server->getPlugin('caldav');
285 285
 				$principalUrl = $node->getPrincipalUrl();
@@ -311,7 +311,7 @@  discard block
 block discarded – undo
311 311
 					]);
312 312
 				}
313 313
 
314
-				$result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
314
+				$result = $this->server->getPropertiesForPath($calendarHomePath.'/'.$uri, [], 1);
315 315
 				if (empty($result)) {
316 316
 					return null;
317 317
 				}
@@ -327,8 +327,8 @@  discard block
 block discarded – undo
327 327
 	 * @param string $principal
328 328
 	 * @return string|null
329 329
 	 */
330
-	protected function getCalendarUserTypeForPrincipal($principal):?string {
331
-		$calendarUserType = '{' . self::NS_CALDAV . '}calendar-user-type';
330
+	protected function getCalendarUserTypeForPrincipal($principal): ?string {
331
+		$calendarUserType = '{'.self::NS_CALDAV.'}calendar-user-type';
332 332
 		$properties = $this->server->getProperties(
333 333
 			$principal,
334 334
 			[$calendarUserType]
@@ -346,7 +346,7 @@  discard block
 block discarded – undo
346 346
 	 * @param ITip\Message $iTipMessage
347 347
 	 * @return null|Property
348 348
 	 */
349
-	private function getCurrentAttendee(ITip\Message $iTipMessage):?Property {
349
+	private function getCurrentAttendee(ITip\Message $iTipMessage): ?Property {
350 350
 		/** @var VEvent $vevent */
351 351
 		$vevent = $iTipMessage->message->VEVENT;
352 352
 		$attendees = $vevent->select('ATTENDEE');
@@ -424,8 +424,8 @@  discard block
 block discarded – undo
424 424
 			['{http://sabredav.org/ns}email-address' => $this->stripOffMailTo($email)],
425 425
 			[
426 426
 				'{DAV:}principal-URL',
427
-				'{' . self::NS_CALDAV . '}calendar-home-set',
428
-				'{' . self::NS_CALDAV . '}schedule-inbox-URL',
427
+				'{'.self::NS_CALDAV.'}calendar-home-set',
428
+				'{'.self::NS_CALDAV.'}schedule-inbox-URL',
429 429
 				'{http://sabredav.org/ns}email-address',
430 430
 
431 431
 			]
@@ -437,7 +437,7 @@  discard block
 block discarded – undo
437 437
 		$objects = [];
438 438
 		$calendarTimeZone = new DateTimeZone('UTC');
439 439
 
440
-		$homePath = $result[0][200]['{' . self::NS_CALDAV . '}calendar-home-set']->getHref();
440
+		$homePath = $result[0][200]['{'.self::NS_CALDAV.'}calendar-home-set']->getHref();
441 441
 		foreach ($this->server->tree->getNodeForPath($homePath)->getChildren() as $node) {
442 442
 			if (!$node instanceof ICalendar) {
443 443
 				continue;
@@ -488,8 +488,8 @@  discard block
 block discarded – undo
488 488
 		}
489 489
 
490 490
 		$inboxProps = $this->server->getProperties(
491
-			$result[0][200]['{' . self::NS_CALDAV . '}schedule-inbox-URL']->getHref(),
492
-			['{' . self::NS_CALDAV . '}calendar-availability']
491
+			$result[0][200]['{'.self::NS_CALDAV.'}schedule-inbox-URL']->getHref(),
492
+			['{'.self::NS_CALDAV.'}calendar-availability']
493 493
 		);
494 494
 
495 495
 		$vcalendar = new VCalendar();
@@ -501,10 +501,10 @@  discard block
 block discarded – undo
501 501
 		$generator->setBaseObject($vcalendar);
502 502
 		$generator->setTimeZone($calendarTimeZone);
503 503
 
504
-		if (isset($inboxProps['{' . self::NS_CALDAV . '}calendar-availability'])) {
504
+		if (isset($inboxProps['{'.self::NS_CALDAV.'}calendar-availability'])) {
505 505
 			$generator->setVAvailability(
506 506
 				Reader::read(
507
-					$inboxProps['{' . self::NS_CALDAV . '}calendar-availability']
507
+					$inboxProps['{'.self::NS_CALDAV.'}calendar-availability']
508 508
 				)
509 509
 			);
510 510
 		}
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
 	 * @return string
549 549
 	 */
550 550
 	private function stripOffMailTo(string $email): string {
551
-		if (stripos($email, 'mailto:')  === 0) {
551
+		if (stripos($email, 'mailto:') === 0) {
552 552
 			return substr($email, 7);
553 553
 		}
554 554
 
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/FilesPlugin.php 2 patches
Indentation   +462 added lines, -462 removed lines patch added patch discarded remove patch
@@ -53,466 +53,466 @@
 block discarded – undo
53 53
 
54 54
 class FilesPlugin extends ServerPlugin {
55 55
 
56
-	// namespace
57
-	public const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
-	public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
59
-	public const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
60
-	public const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
61
-	public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
62
-	public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
63
-	public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
64
-	public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
65
-	public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
66
-	public const GETETAG_PROPERTYNAME = '{DAV:}getetag';
67
-	public const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
68
-	public const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
69
-	public const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
70
-	public const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
71
-	public const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
72
-	public const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
73
-	public const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type';
74
-	public const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted';
75
-	public const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag';
76
-	public const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time';
77
-	public const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time';
78
-	public const SHARE_NOTE = '{http://nextcloud.org/ns}note';
79
-
80
-	/**
81
-	 * Reference to main server object
82
-	 *
83
-	 * @var \Sabre\DAV\Server
84
-	 */
85
-	private $server;
86
-
87
-	/**
88
-	 * @var Tree
89
-	 */
90
-	private $tree;
91
-
92
-	/**
93
-	 * Whether this is public webdav.
94
-	 * If true, some returned information will be stripped off.
95
-	 *
96
-	 * @var bool
97
-	 */
98
-	private $isPublic;
99
-
100
-	/**
101
-	 * @var bool
102
-	 */
103
-	private $downloadAttachment;
104
-
105
-	/**
106
-	 * @var IConfig
107
-	 */
108
-	private $config;
109
-
110
-	/**
111
-	 * @var IRequest
112
-	 */
113
-	private $request;
114
-
115
-	/**
116
-	 * @var IPreview
117
-	 */
118
-	private $previewManager;
119
-
120
-	/**
121
-	 * @param Tree $tree
122
-	 * @param IConfig $config
123
-	 * @param IRequest $request
124
-	 * @param IPreview $previewManager
125
-	 * @param bool $isPublic
126
-	 * @param bool $downloadAttachment
127
-	 */
128
-	public function __construct(Tree $tree,
129
-								IConfig $config,
130
-								IRequest $request,
131
-								IPreview $previewManager,
132
-								$isPublic = false,
133
-								$downloadAttachment = true) {
134
-		$this->tree = $tree;
135
-		$this->config = $config;
136
-		$this->request = $request;
137
-		$this->isPublic = $isPublic;
138
-		$this->downloadAttachment = $downloadAttachment;
139
-		$this->previewManager = $previewManager;
140
-	}
141
-
142
-	/**
143
-	 * This initializes the plugin.
144
-	 *
145
-	 * This function is called by \Sabre\DAV\Server, after
146
-	 * addPlugin is called.
147
-	 *
148
-	 * This method should set up the required event subscriptions.
149
-	 *
150
-	 * @param \Sabre\DAV\Server $server
151
-	 * @return void
152
-	 */
153
-	public function initialize(\Sabre\DAV\Server $server) {
154
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
155
-		$server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
156
-		$server->protectedProperties[] = self::FILEID_PROPERTYNAME;
157
-		$server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
158
-		$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
159
-		$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
160
-		$server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
161
-		$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
162
-		$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
163
-		$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
164
-		$server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
165
-		$server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
166
-		$server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
167
-		$server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
168
-		$server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME;
169
-		$server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME;
170
-		$server->protectedProperties[] = self::SHARE_NOTE;
171
-
172
-		// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
173
-		$allowedProperties = ['{DAV:}getetag'];
174
-		$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
175
-
176
-		$this->server = $server;
177
-		$this->server->on('propFind', [$this, 'handleGetProperties']);
178
-		$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
179
-		$this->server->on('afterBind', [$this, 'sendFileIdHeader']);
180
-		$this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']);
181
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
182
-		$this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']);
183
-		$this->server->on('afterResponse', function ($request, ResponseInterface $response) {
184
-			$body = $response->getBody();
185
-			if (is_resource($body)) {
186
-				fclose($body);
187
-			}
188
-		});
189
-		$this->server->on('beforeMove', [$this, 'checkMove']);
190
-	}
191
-
192
-	/**
193
-	 * Plugin that checks if a move can actually be performed.
194
-	 *
195
-	 * @param string $source source path
196
-	 * @param string $destination destination path
197
-	 * @throws Forbidden
198
-	 * @throws NotFound
199
-	 */
200
-	public function checkMove($source, $destination) {
201
-		$sourceNode = $this->tree->getNodeForPath($source);
202
-		if (!$sourceNode instanceof Node) {
203
-			return;
204
-		}
205
-		list($sourceDir,) = \Sabre\Uri\split($source);
206
-		list($destinationDir,) = \Sabre\Uri\split($destination);
207
-
208
-		if ($sourceDir !== $destinationDir) {
209
-			$sourceNodeFileInfo = $sourceNode->getFileInfo();
210
-			if ($sourceNodeFileInfo === null) {
211
-				throw new NotFound($source . ' does not exist');
212
-			}
213
-
214
-			if (!$sourceNodeFileInfo->isDeletable()) {
215
-				throw new Forbidden($source . " cannot be deleted");
216
-			}
217
-		}
218
-	}
219
-
220
-	/**
221
-	 * This sets a cookie to be able to recognize the start of the download
222
-	 * the content must not be longer than 32 characters and must only contain
223
-	 * alphanumeric characters
224
-	 *
225
-	 * @param RequestInterface $request
226
-	 * @param ResponseInterface $response
227
-	 */
228
-	public function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
229
-		$queryParams = $request->getQueryParameters();
230
-
231
-		/**
232
-		 * this sets a cookie to be able to recognize the start of the download
233
-		 * the content must not be longer than 32 characters and must only contain
234
-		 * alphanumeric characters
235
-		 */
236
-		if (isset($queryParams['downloadStartSecret'])) {
237
-			$token = $queryParams['downloadStartSecret'];
238
-			if (!isset($token[32])
239
-				&& preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
240
-				// FIXME: use $response->setHeader() instead
241
-				setcookie('ocDownloadStarted', $token, time() + 20, '/');
242
-			}
243
-		}
244
-	}
245
-
246
-	/**
247
-	 * Add headers to file download
248
-	 *
249
-	 * @param RequestInterface $request
250
-	 * @param ResponseInterface $response
251
-	 */
252
-	public function httpGet(RequestInterface $request, ResponseInterface $response) {
253
-		// Only handle valid files
254
-		$node = $this->tree->getNodeForPath($request->getPath());
255
-		if (!($node instanceof IFile)) {
256
-			return;
257
-		}
258
-
259
-		// adds a 'Content-Disposition: attachment' header in case no disposition
260
-		// header has been set before
261
-		if ($this->downloadAttachment &&
262
-			$response->getHeader('Content-Disposition') === null) {
263
-			$filename = $node->getName();
264
-			if ($this->request->isUserAgent(
265
-				[
266
-					Request::USER_AGENT_IE,
267
-					Request::USER_AGENT_ANDROID_MOBILE_CHROME,
268
-					Request::USER_AGENT_FREEBOX,
269
-				])) {
270
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
271
-			} else {
272
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
273
-													 . '; filename="' . rawurlencode($filename) . '"');
274
-			}
275
-		}
276
-
277
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
278
-			//Add OC-Checksum header
279
-			$checksum = $node->getChecksum();
280
-			if ($checksum !== null && $checksum !== '') {
281
-				$response->addHeader('OC-Checksum', $checksum);
282
-			}
283
-		}
284
-	}
285
-
286
-	/**
287
-	 * Adds all ownCloud-specific properties
288
-	 *
289
-	 * @param PropFind $propFind
290
-	 * @param \Sabre\DAV\INode $node
291
-	 * @return void
292
-	 */
293
-	public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
294
-		$httpRequest = $this->server->httpRequest;
295
-
296
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
297
-			/**
298
-			 * This was disabled, because it made dir listing throw an exception,
299
-			 * so users were unable to navigate into folders where one subitem
300
-			 * is blocked by the files_accesscontrol app, see:
301
-			 * https://github.com/nextcloud/files_accesscontrol/issues/65
302
-			 * if (!$node->getFileInfo()->isReadable()) {
303
-			 *     // avoid detecting files through this means
304
-			 *     throw new NotFound();
305
-			 * }
306
-			 */
307
-
308
-			$propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) {
309
-				return $node->getFileId();
310
-			});
311
-
312
-			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) {
313
-				return $node->getInternalFileId();
314
-			});
315
-
316
-			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) {
317
-				$perms = $node->getDavPermissions();
318
-				if ($this->isPublic) {
319
-					// remove mount information
320
-					$perms = str_replace(['S', 'M'], '', $perms);
321
-				}
322
-				return $perms;
323
-			});
324
-
325
-			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
326
-				return $node->getSharePermissions(
327
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
328
-				);
329
-			});
330
-
331
-			$propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
332
-				$ncPermissions = $node->getSharePermissions(
333
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
334
-				);
335
-				$ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions);
336
-				return json_encode($ocmPermissions);
337
-			});
338
-
339
-			$propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node) {
340
-				return $node->getETag();
341
-			});
342
-
343
-			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node) {
344
-				$owner = $node->getOwner();
345
-				if (!$owner) {
346
-					return null;
347
-				} else {
348
-					return $owner->getUID();
349
-				}
350
-			});
351
-			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node) {
352
-				$owner = $node->getOwner();
353
-				if (!$owner) {
354
-					return null;
355
-				} else {
356
-					return $owner->getDisplayName();
357
-				}
358
-			});
359
-
360
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
361
-				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
362
-			});
363
-			$propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
364
-				return $node->getSize();
365
-			});
366
-			$propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) {
367
-				return $node->getFileInfo()->getMountPoint()->getMountType();
368
-			});
369
-
370
-			$propFind->handle(self::SHARE_NOTE, function () use ($node, $httpRequest) {
371
-				return $node->getNoteFromShare(
372
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
373
-				);
374
-			});
375
-		}
376
-
377
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
378
-			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use ($node) {
379
-				return $this->config->getSystemValue('data-fingerprint', '');
380
-			});
381
-		}
382
-
383
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
384
-			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) {
385
-				try {
386
-					$directDownloadUrl = $node->getDirectDownload();
387
-					if (isset($directDownloadUrl['url'])) {
388
-						return $directDownloadUrl['url'];
389
-					}
390
-				} catch (StorageNotAvailableException $e) {
391
-					return false;
392
-				} catch (ForbiddenException $e) {
393
-					return false;
394
-				}
395
-				return false;
396
-			});
397
-
398
-			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) {
399
-				$checksum = $node->getChecksum();
400
-				if ($checksum === null || $checksum === '') {
401
-					return null;
402
-				}
403
-
404
-				return new ChecksumList($checksum);
405
-			});
406
-
407
-			$propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) {
408
-				return $node->getFileInfo()->getCreationTime();
409
-			});
410
-
411
-			$propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) {
412
-				return $node->getFileInfo()->getUploadTime();
413
-			});
414
-		}
415
-
416
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
417
-			$propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
418
-				return $node->getSize();
419
-			});
420
-
421
-			$propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function () use ($node) {
422
-				return $node->getFileInfo()->isEncrypted() ? '1' : '0';
423
-			});
424
-		}
425
-	}
426
-
427
-	/**
428
-	 * translate Nextcloud permissions to OCM Permissions
429
-	 *
430
-	 * @param $ncPermissions
431
-	 * @return array
432
-	 */
433
-	protected function ncPermissions2ocmPermissions($ncPermissions) {
434
-		$ocmPermissions = [];
435
-
436
-		if ($ncPermissions & Constants::PERMISSION_SHARE) {
437
-			$ocmPermissions[] = 'share';
438
-		}
439
-
440
-		if ($ncPermissions & Constants::PERMISSION_READ) {
441
-			$ocmPermissions[] = 'read';
442
-		}
443
-
444
-		if (($ncPermissions & Constants::PERMISSION_CREATE) ||
445
-			($ncPermissions & Constants::PERMISSION_UPDATE)) {
446
-			$ocmPermissions[] = 'write';
447
-		}
448
-
449
-		return $ocmPermissions;
450
-	}
451
-
452
-	/**
453
-	 * Update ownCloud-specific properties
454
-	 *
455
-	 * @param string $path
456
-	 * @param PropPatch $propPatch
457
-	 *
458
-	 * @return void
459
-	 */
460
-	public function handleUpdateProperties($path, PropPatch $propPatch) {
461
-		$node = $this->tree->getNodeForPath($path);
462
-		if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) {
463
-			return;
464
-		}
465
-
466
-		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) {
467
-			if (empty($time)) {
468
-				return false;
469
-			}
470
-			$node->touch($time);
471
-			return true;
472
-		});
473
-		$propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) {
474
-			if (empty($etag)) {
475
-				return false;
476
-			}
477
-			if ($node->setEtag($etag) !== -1) {
478
-				return true;
479
-			}
480
-			return false;
481
-		});
482
-		$propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) {
483
-			if (empty($time)) {
484
-				return false;
485
-			}
486
-			$node->setCreationTime((int) $time);
487
-			return true;
488
-		});
489
-	}
490
-
491
-	/**
492
-	 * @param string $filePath
493
-	 * @param \Sabre\DAV\INode $node
494
-	 * @throws \Sabre\DAV\Exception\BadRequest
495
-	 */
496
-	public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
497
-		// chunked upload handling
498
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
499
-			list($path, $name) = \Sabre\Uri\split($filePath);
500
-			$info = \OC_FileChunking::decodeName($name);
501
-			if (!empty($info)) {
502
-				$filePath = $path . '/' . $info['name'];
503
-			}
504
-		}
505
-
506
-		// we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
507
-		if (!$this->server->tree->nodeExists($filePath)) {
508
-			return;
509
-		}
510
-		$node = $this->server->tree->getNodeForPath($filePath);
511
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
512
-			$fileId = $node->getFileId();
513
-			if (!is_null($fileId)) {
514
-				$this->server->httpResponse->setHeader('OC-FileId', $fileId);
515
-			}
516
-		}
517
-	}
56
+    // namespace
57
+    public const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
+    public const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
59
+    public const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
60
+    public const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
61
+    public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
62
+    public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
63
+    public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
64
+    public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
65
+    public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
66
+    public const GETETAG_PROPERTYNAME = '{DAV:}getetag';
67
+    public const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
68
+    public const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
69
+    public const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
70
+    public const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
71
+    public const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
72
+    public const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
73
+    public const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type';
74
+    public const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted';
75
+    public const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag';
76
+    public const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time';
77
+    public const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time';
78
+    public const SHARE_NOTE = '{http://nextcloud.org/ns}note';
79
+
80
+    /**
81
+     * Reference to main server object
82
+     *
83
+     * @var \Sabre\DAV\Server
84
+     */
85
+    private $server;
86
+
87
+    /**
88
+     * @var Tree
89
+     */
90
+    private $tree;
91
+
92
+    /**
93
+     * Whether this is public webdav.
94
+     * If true, some returned information will be stripped off.
95
+     *
96
+     * @var bool
97
+     */
98
+    private $isPublic;
99
+
100
+    /**
101
+     * @var bool
102
+     */
103
+    private $downloadAttachment;
104
+
105
+    /**
106
+     * @var IConfig
107
+     */
108
+    private $config;
109
+
110
+    /**
111
+     * @var IRequest
112
+     */
113
+    private $request;
114
+
115
+    /**
116
+     * @var IPreview
117
+     */
118
+    private $previewManager;
119
+
120
+    /**
121
+     * @param Tree $tree
122
+     * @param IConfig $config
123
+     * @param IRequest $request
124
+     * @param IPreview $previewManager
125
+     * @param bool $isPublic
126
+     * @param bool $downloadAttachment
127
+     */
128
+    public function __construct(Tree $tree,
129
+                                IConfig $config,
130
+                                IRequest $request,
131
+                                IPreview $previewManager,
132
+                                $isPublic = false,
133
+                                $downloadAttachment = true) {
134
+        $this->tree = $tree;
135
+        $this->config = $config;
136
+        $this->request = $request;
137
+        $this->isPublic = $isPublic;
138
+        $this->downloadAttachment = $downloadAttachment;
139
+        $this->previewManager = $previewManager;
140
+    }
141
+
142
+    /**
143
+     * This initializes the plugin.
144
+     *
145
+     * This function is called by \Sabre\DAV\Server, after
146
+     * addPlugin is called.
147
+     *
148
+     * This method should set up the required event subscriptions.
149
+     *
150
+     * @param \Sabre\DAV\Server $server
151
+     * @return void
152
+     */
153
+    public function initialize(\Sabre\DAV\Server $server) {
154
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
155
+        $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
156
+        $server->protectedProperties[] = self::FILEID_PROPERTYNAME;
157
+        $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
158
+        $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
159
+        $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
160
+        $server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
161
+        $server->protectedProperties[] = self::SIZE_PROPERTYNAME;
162
+        $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
163
+        $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
164
+        $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
165
+        $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
166
+        $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
167
+        $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
168
+        $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME;
169
+        $server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME;
170
+        $server->protectedProperties[] = self::SHARE_NOTE;
171
+
172
+        // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
173
+        $allowedProperties = ['{DAV:}getetag'];
174
+        $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
175
+
176
+        $this->server = $server;
177
+        $this->server->on('propFind', [$this, 'handleGetProperties']);
178
+        $this->server->on('propPatch', [$this, 'handleUpdateProperties']);
179
+        $this->server->on('afterBind', [$this, 'sendFileIdHeader']);
180
+        $this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']);
181
+        $this->server->on('afterMethod:GET', [$this,'httpGet']);
182
+        $this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']);
183
+        $this->server->on('afterResponse', function ($request, ResponseInterface $response) {
184
+            $body = $response->getBody();
185
+            if (is_resource($body)) {
186
+                fclose($body);
187
+            }
188
+        });
189
+        $this->server->on('beforeMove', [$this, 'checkMove']);
190
+    }
191
+
192
+    /**
193
+     * Plugin that checks if a move can actually be performed.
194
+     *
195
+     * @param string $source source path
196
+     * @param string $destination destination path
197
+     * @throws Forbidden
198
+     * @throws NotFound
199
+     */
200
+    public function checkMove($source, $destination) {
201
+        $sourceNode = $this->tree->getNodeForPath($source);
202
+        if (!$sourceNode instanceof Node) {
203
+            return;
204
+        }
205
+        list($sourceDir,) = \Sabre\Uri\split($source);
206
+        list($destinationDir,) = \Sabre\Uri\split($destination);
207
+
208
+        if ($sourceDir !== $destinationDir) {
209
+            $sourceNodeFileInfo = $sourceNode->getFileInfo();
210
+            if ($sourceNodeFileInfo === null) {
211
+                throw new NotFound($source . ' does not exist');
212
+            }
213
+
214
+            if (!$sourceNodeFileInfo->isDeletable()) {
215
+                throw new Forbidden($source . " cannot be deleted");
216
+            }
217
+        }
218
+    }
219
+
220
+    /**
221
+     * This sets a cookie to be able to recognize the start of the download
222
+     * the content must not be longer than 32 characters and must only contain
223
+     * alphanumeric characters
224
+     *
225
+     * @param RequestInterface $request
226
+     * @param ResponseInterface $response
227
+     */
228
+    public function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
229
+        $queryParams = $request->getQueryParameters();
230
+
231
+        /**
232
+         * this sets a cookie to be able to recognize the start of the download
233
+         * the content must not be longer than 32 characters and must only contain
234
+         * alphanumeric characters
235
+         */
236
+        if (isset($queryParams['downloadStartSecret'])) {
237
+            $token = $queryParams['downloadStartSecret'];
238
+            if (!isset($token[32])
239
+                && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
240
+                // FIXME: use $response->setHeader() instead
241
+                setcookie('ocDownloadStarted', $token, time() + 20, '/');
242
+            }
243
+        }
244
+    }
245
+
246
+    /**
247
+     * Add headers to file download
248
+     *
249
+     * @param RequestInterface $request
250
+     * @param ResponseInterface $response
251
+     */
252
+    public function httpGet(RequestInterface $request, ResponseInterface $response) {
253
+        // Only handle valid files
254
+        $node = $this->tree->getNodeForPath($request->getPath());
255
+        if (!($node instanceof IFile)) {
256
+            return;
257
+        }
258
+
259
+        // adds a 'Content-Disposition: attachment' header in case no disposition
260
+        // header has been set before
261
+        if ($this->downloadAttachment &&
262
+            $response->getHeader('Content-Disposition') === null) {
263
+            $filename = $node->getName();
264
+            if ($this->request->isUserAgent(
265
+                [
266
+                    Request::USER_AGENT_IE,
267
+                    Request::USER_AGENT_ANDROID_MOBILE_CHROME,
268
+                    Request::USER_AGENT_FREEBOX,
269
+                ])) {
270
+                $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
271
+            } else {
272
+                $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
273
+                                                        . '; filename="' . rawurlencode($filename) . '"');
274
+            }
275
+        }
276
+
277
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
278
+            //Add OC-Checksum header
279
+            $checksum = $node->getChecksum();
280
+            if ($checksum !== null && $checksum !== '') {
281
+                $response->addHeader('OC-Checksum', $checksum);
282
+            }
283
+        }
284
+    }
285
+
286
+    /**
287
+     * Adds all ownCloud-specific properties
288
+     *
289
+     * @param PropFind $propFind
290
+     * @param \Sabre\DAV\INode $node
291
+     * @return void
292
+     */
293
+    public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
294
+        $httpRequest = $this->server->httpRequest;
295
+
296
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
297
+            /**
298
+             * This was disabled, because it made dir listing throw an exception,
299
+             * so users were unable to navigate into folders where one subitem
300
+             * is blocked by the files_accesscontrol app, see:
301
+             * https://github.com/nextcloud/files_accesscontrol/issues/65
302
+             * if (!$node->getFileInfo()->isReadable()) {
303
+             *     // avoid detecting files through this means
304
+             *     throw new NotFound();
305
+             * }
306
+             */
307
+
308
+            $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) {
309
+                return $node->getFileId();
310
+            });
311
+
312
+            $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) {
313
+                return $node->getInternalFileId();
314
+            });
315
+
316
+            $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) {
317
+                $perms = $node->getDavPermissions();
318
+                if ($this->isPublic) {
319
+                    // remove mount information
320
+                    $perms = str_replace(['S', 'M'], '', $perms);
321
+                }
322
+                return $perms;
323
+            });
324
+
325
+            $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
326
+                return $node->getSharePermissions(
327
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
328
+                );
329
+            });
330
+
331
+            $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
332
+                $ncPermissions = $node->getSharePermissions(
333
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
334
+                );
335
+                $ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions);
336
+                return json_encode($ocmPermissions);
337
+            });
338
+
339
+            $propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node) {
340
+                return $node->getETag();
341
+            });
342
+
343
+            $propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node) {
344
+                $owner = $node->getOwner();
345
+                if (!$owner) {
346
+                    return null;
347
+                } else {
348
+                    return $owner->getUID();
349
+                }
350
+            });
351
+            $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node) {
352
+                $owner = $node->getOwner();
353
+                if (!$owner) {
354
+                    return null;
355
+                } else {
356
+                    return $owner->getDisplayName();
357
+                }
358
+            });
359
+
360
+            $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
361
+                return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
362
+            });
363
+            $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
364
+                return $node->getSize();
365
+            });
366
+            $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) {
367
+                return $node->getFileInfo()->getMountPoint()->getMountType();
368
+            });
369
+
370
+            $propFind->handle(self::SHARE_NOTE, function () use ($node, $httpRequest) {
371
+                return $node->getNoteFromShare(
372
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
373
+                );
374
+            });
375
+        }
376
+
377
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
378
+            $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use ($node) {
379
+                return $this->config->getSystemValue('data-fingerprint', '');
380
+            });
381
+        }
382
+
383
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
384
+            $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) {
385
+                try {
386
+                    $directDownloadUrl = $node->getDirectDownload();
387
+                    if (isset($directDownloadUrl['url'])) {
388
+                        return $directDownloadUrl['url'];
389
+                    }
390
+                } catch (StorageNotAvailableException $e) {
391
+                    return false;
392
+                } catch (ForbiddenException $e) {
393
+                    return false;
394
+                }
395
+                return false;
396
+            });
397
+
398
+            $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) {
399
+                $checksum = $node->getChecksum();
400
+                if ($checksum === null || $checksum === '') {
401
+                    return null;
402
+                }
403
+
404
+                return new ChecksumList($checksum);
405
+            });
406
+
407
+            $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) {
408
+                return $node->getFileInfo()->getCreationTime();
409
+            });
410
+
411
+            $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) {
412
+                return $node->getFileInfo()->getUploadTime();
413
+            });
414
+        }
415
+
416
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
417
+            $propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
418
+                return $node->getSize();
419
+            });
420
+
421
+            $propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function () use ($node) {
422
+                return $node->getFileInfo()->isEncrypted() ? '1' : '0';
423
+            });
424
+        }
425
+    }
426
+
427
+    /**
428
+     * translate Nextcloud permissions to OCM Permissions
429
+     *
430
+     * @param $ncPermissions
431
+     * @return array
432
+     */
433
+    protected function ncPermissions2ocmPermissions($ncPermissions) {
434
+        $ocmPermissions = [];
435
+
436
+        if ($ncPermissions & Constants::PERMISSION_SHARE) {
437
+            $ocmPermissions[] = 'share';
438
+        }
439
+
440
+        if ($ncPermissions & Constants::PERMISSION_READ) {
441
+            $ocmPermissions[] = 'read';
442
+        }
443
+
444
+        if (($ncPermissions & Constants::PERMISSION_CREATE) ||
445
+            ($ncPermissions & Constants::PERMISSION_UPDATE)) {
446
+            $ocmPermissions[] = 'write';
447
+        }
448
+
449
+        return $ocmPermissions;
450
+    }
451
+
452
+    /**
453
+     * Update ownCloud-specific properties
454
+     *
455
+     * @param string $path
456
+     * @param PropPatch $propPatch
457
+     *
458
+     * @return void
459
+     */
460
+    public function handleUpdateProperties($path, PropPatch $propPatch) {
461
+        $node = $this->tree->getNodeForPath($path);
462
+        if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) {
463
+            return;
464
+        }
465
+
466
+        $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) {
467
+            if (empty($time)) {
468
+                return false;
469
+            }
470
+            $node->touch($time);
471
+            return true;
472
+        });
473
+        $propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) {
474
+            if (empty($etag)) {
475
+                return false;
476
+            }
477
+            if ($node->setEtag($etag) !== -1) {
478
+                return true;
479
+            }
480
+            return false;
481
+        });
482
+        $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) {
483
+            if (empty($time)) {
484
+                return false;
485
+            }
486
+            $node->setCreationTime((int) $time);
487
+            return true;
488
+        });
489
+    }
490
+
491
+    /**
492
+     * @param string $filePath
493
+     * @param \Sabre\DAV\INode $node
494
+     * @throws \Sabre\DAV\Exception\BadRequest
495
+     */
496
+    public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
497
+        // chunked upload handling
498
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
499
+            list($path, $name) = \Sabre\Uri\split($filePath);
500
+            $info = \OC_FileChunking::decodeName($name);
501
+            if (!empty($info)) {
502
+                $filePath = $path . '/' . $info['name'];
503
+            }
504
+        }
505
+
506
+        // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
507
+        if (!$this->server->tree->nodeExists($filePath)) {
508
+            return;
509
+        }
510
+        $node = $this->server->tree->getNodeForPath($filePath);
511
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
512
+            $fileId = $node->getFileId();
513
+            if (!is_null($fileId)) {
514
+                $this->server->httpResponse->setHeader('OC-FileId', $fileId);
515
+            }
516
+        }
517
+    }
518 518
 }
Please login to merge, or discard this patch.
Spacing   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -178,9 +178,9 @@  discard block
 block discarded – undo
178 178
 		$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
179 179
 		$this->server->on('afterBind', [$this, 'sendFileIdHeader']);
180 180
 		$this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']);
181
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
181
+		$this->server->on('afterMethod:GET', [$this, 'httpGet']);
182 182
 		$this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']);
183
-		$this->server->on('afterResponse', function ($request, ResponseInterface $response) {
183
+		$this->server->on('afterResponse', function($request, ResponseInterface $response) {
184 184
 			$body = $response->getBody();
185 185
 			if (is_resource($body)) {
186 186
 				fclose($body);
@@ -208,11 +208,11 @@  discard block
 block discarded – undo
208 208
 		if ($sourceDir !== $destinationDir) {
209 209
 			$sourceNodeFileInfo = $sourceNode->getFileInfo();
210 210
 			if ($sourceNodeFileInfo === null) {
211
-				throw new NotFound($source . ' does not exist');
211
+				throw new NotFound($source.' does not exist');
212 212
 			}
213 213
 
214 214
 			if (!$sourceNodeFileInfo->isDeletable()) {
215
-				throw new Forbidden($source . " cannot be deleted");
215
+				throw new Forbidden($source." cannot be deleted");
216 216
 			}
217 217
 		}
218 218
 	}
@@ -267,10 +267,10 @@  discard block
 block discarded – undo
267 267
 					Request::USER_AGENT_ANDROID_MOBILE_CHROME,
268 268
 					Request::USER_AGENT_FREEBOX,
269 269
 				])) {
270
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
270
+				$response->addHeader('Content-Disposition', 'attachment; filename="'.rawurlencode($filename).'"');
271 271
 			} else {
272
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
273
-													 . '; filename="' . rawurlencode($filename) . '"');
272
+				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\''.rawurlencode($filename)
273
+													 . '; filename="'.rawurlencode($filename).'"');
274 274
 			}
275 275
 		}
276 276
 
@@ -305,15 +305,15 @@  discard block
 block discarded – undo
305 305
 			 * }
306 306
 			 */
307 307
 
308
-			$propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node) {
308
+			$propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
309 309
 				return $node->getFileId();
310 310
 			});
311 311
 
312
-			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) {
312
+			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
313 313
 				return $node->getInternalFileId();
314 314
 			});
315 315
 
316
-			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) {
316
+			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
317 317
 				$perms = $node->getDavPermissions();
318 318
 				if ($this->isPublic) {
319 319
 					// remove mount information
@@ -322,13 +322,13 @@  discard block
 block discarded – undo
322 322
 				return $perms;
323 323
 			});
324 324
 
325
-			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
325
+			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
326 326
 				return $node->getSharePermissions(
327 327
 					$httpRequest->getRawServerValue('PHP_AUTH_USER')
328 328
 				);
329 329
 			});
330 330
 
331
-			$propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function () use ($node, $httpRequest) {
331
+			$propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
332 332
 				$ncPermissions = $node->getSharePermissions(
333 333
 					$httpRequest->getRawServerValue('PHP_AUTH_USER')
334 334
 				);
@@ -336,11 +336,11 @@  discard block
 block discarded – undo
336 336
 				return json_encode($ocmPermissions);
337 337
 			});
338 338
 
339
-			$propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node) {
339
+			$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
340 340
 				return $node->getETag();
341 341
 			});
342 342
 
343
-			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function () use ($node) {
343
+			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
344 344
 				$owner = $node->getOwner();
345 345
 				if (!$owner) {
346 346
 					return null;
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
 					return $owner->getUID();
349 349
 				}
350 350
 			});
351
-			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function () use ($node) {
351
+			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
352 352
 				$owner = $node->getOwner();
353 353
 				if (!$owner) {
354 354
 					return null;
@@ -357,17 +357,17 @@  discard block
 block discarded – undo
357 357
 				}
358 358
 			});
359 359
 
360
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
360
+			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function() use ($node) {
361 361
 				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
362 362
 			});
363
-			$propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
363
+			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
364 364
 				return $node->getSize();
365 365
 			});
366
-			$propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) {
366
+			$propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function() use ($node) {
367 367
 				return $node->getFileInfo()->getMountPoint()->getMountType();
368 368
 			});
369 369
 
370
-			$propFind->handle(self::SHARE_NOTE, function () use ($node, $httpRequest) {
370
+			$propFind->handle(self::SHARE_NOTE, function() use ($node, $httpRequest) {
371 371
 				return $node->getNoteFromShare(
372 372
 					$httpRequest->getRawServerValue('PHP_AUTH_USER')
373 373
 				);
@@ -375,13 +375,13 @@  discard block
 block discarded – undo
375 375
 		}
376 376
 
377 377
 		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
378
-			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function () use ($node) {
378
+			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
379 379
 				return $this->config->getSystemValue('data-fingerprint', '');
380 380
 			});
381 381
 		}
382 382
 
383 383
 		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
384
-			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function () use ($node) {
384
+			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
385 385
 				try {
386 386
 					$directDownloadUrl = $node->getDirectDownload();
387 387
 					if (isset($directDownloadUrl['url'])) {
@@ -395,7 +395,7 @@  discard block
 block discarded – undo
395 395
 				return false;
396 396
 			});
397 397
 
398
-			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) {
398
+			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
399 399
 				$checksum = $node->getChecksum();
400 400
 				if ($checksum === null || $checksum === '') {
401 401
 					return null;
@@ -404,21 +404,21 @@  discard block
 block discarded – undo
404 404
 				return new ChecksumList($checksum);
405 405
 			});
406 406
 
407
-			$propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) {
407
+			$propFind->handle(self::CREATION_TIME_PROPERTYNAME, function() use ($node) {
408 408
 				return $node->getFileInfo()->getCreationTime();
409 409
 			});
410 410
 
411
-			$propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) {
411
+			$propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function() use ($node) {
412 412
 				return $node->getFileInfo()->getUploadTime();
413 413
 			});
414 414
 		}
415 415
 
416 416
 		if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
417
-			$propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node) {
417
+			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
418 418
 				return $node->getSize();
419 419
 			});
420 420
 
421
-			$propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function () use ($node) {
421
+			$propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function() use ($node) {
422 422
 				return $node->getFileInfo()->isEncrypted() ? '1' : '0';
423 423
 			});
424 424
 		}
@@ -463,14 +463,14 @@  discard block
 block discarded – undo
463 463
 			return;
464 464
 		}
465 465
 
466
-		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function ($time) use ($node) {
466
+		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($node) {
467 467
 			if (empty($time)) {
468 468
 				return false;
469 469
 			}
470 470
 			$node->touch($time);
471 471
 			return true;
472 472
 		});
473
-		$propPatch->handle(self::GETETAG_PROPERTYNAME, function ($etag) use ($node) {
473
+		$propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($node) {
474 474
 			if (empty($etag)) {
475 475
 				return false;
476 476
 			}
@@ -479,7 +479,7 @@  discard block
 block discarded – undo
479 479
 			}
480 480
 			return false;
481 481
 		});
482
-		$propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function ($time) use ($node) {
482
+		$propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function($time) use ($node) {
483 483
 			if (empty($time)) {
484 484
 				return false;
485 485
 			}
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
 			list($path, $name) = \Sabre\Uri\split($filePath);
500 500
 			$info = \OC_FileChunking::decodeName($name);
501 501
 			if (!empty($info)) {
502
-				$filePath = $path . '/' . $info['name'];
502
+				$filePath = $path.'/'.$info['name'];
503 503
 			}
504 504
 		}
505 505
 
Please login to merge, or discard this patch.
apps/dav/lib/CardDAV/AddressBookImpl.php 1 patch
Indentation   +285 added lines, -285 removed lines patch added patch discarded remove patch
@@ -40,289 +40,289 @@
 block discarded – undo
40 40
 
41 41
 class AddressBookImpl implements IAddressBook {
42 42
 
43
-	/** @var CardDavBackend */
44
-	private $backend;
45
-
46
-	/** @var array */
47
-	private $addressBookInfo;
48
-
49
-	/** @var AddressBook */
50
-	private $addressBook;
51
-
52
-	/** @var IURLGenerator */
53
-	private $urlGenerator;
54
-
55
-	/**
56
-	 * AddressBookImpl constructor.
57
-	 *
58
-	 * @param AddressBook $addressBook
59
-	 * @param array $addressBookInfo
60
-	 * @param CardDavBackend $backend
61
-	 * @param IUrlGenerator $urlGenerator
62
-	 */
63
-	public function __construct(
64
-			AddressBook $addressBook,
65
-			array $addressBookInfo,
66
-			CardDavBackend $backend,
67
-			IURLGenerator $urlGenerator) {
68
-		$this->addressBook = $addressBook;
69
-		$this->addressBookInfo = $addressBookInfo;
70
-		$this->backend = $backend;
71
-		$this->urlGenerator = $urlGenerator;
72
-	}
73
-
74
-	/**
75
-	 * @return string defining the technical unique key
76
-	 * @since 5.0.0
77
-	 */
78
-	public function getKey() {
79
-		return $this->addressBookInfo['id'];
80
-	}
81
-
82
-	/**
83
-	 * @return string defining the unique uri
84
-	 * @since 16.0.0
85
-	 */
86
-	public function getUri(): string {
87
-		return $this->addressBookInfo['uri'];
88
-	}
89
-
90
-	/**
91
-	 * In comparison to getKey() this function returns a human readable (maybe translated) name
92
-	 *
93
-	 * @return mixed
94
-	 * @since 5.0.0
95
-	 */
96
-	public function getDisplayName() {
97
-		return $this->addressBookInfo['{DAV:}displayname'];
98
-	}
99
-
100
-	/**
101
-	 * @param string $pattern which should match within the $searchProperties
102
-	 * @param array $searchProperties defines the properties within the query pattern should match
103
-	 * @param array $options Options to define the output format and search behavior
104
-	 * 	- 'types' boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
105
-	 *    example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => '[email protected]']]
106
-	 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
107
-	 * 	- 'limit' - Set a numeric limit for the search results
108
-	 * 	- 'offset' - Set the offset for the limited search results
109
-	 * @return array an array of contacts which are arrays of key-value-pairs
110
-	 *  example result:
111
-	 *  [
112
-	 *		['id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => '[email protected]', 'GEO' => '37.386013;-122.082932'],
113
-	 *		['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['[email protected]', '[email protected]']]
114
-	 *	]
115
-	 * @since 5.0.0
116
-	 */
117
-	public function search($pattern, $searchProperties, $options) {
118
-		$results = $this->backend->search($this->getKey(), $pattern, $searchProperties, $options);
119
-
120
-		$withTypes = \array_key_exists('types', $options) && $options['types'] === true;
121
-
122
-		$vCards = [];
123
-		foreach ($results as $result) {
124
-			$vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']), $withTypes);
125
-		}
126
-
127
-		return $vCards;
128
-	}
129
-
130
-	/**
131
-	 * @param array $properties this array if key-value-pairs defines a contact
132
-	 * @return array an array representing the contact just created or updated
133
-	 * @since 5.0.0
134
-	 */
135
-	public function createOrUpdate($properties) {
136
-		$update = false;
137
-		if (!isset($properties['URI'])) { // create a new contact
138
-			$uid = $this->createUid();
139
-			$uri = $uid . '.vcf';
140
-			$vCard = $this->createEmptyVCard($uid);
141
-		} else { // update existing contact
142
-			$uri = $properties['URI'];
143
-			$vCardData = $this->backend->getCard($this->getKey(), $uri);
144
-			$vCard = $this->readCard($vCardData['carddata']);
145
-			$update = true;
146
-		}
147
-
148
-		foreach ($properties as $key => $value) {
149
-			$vCard->$key = $vCard->createProperty($key, $value);
150
-		}
151
-
152
-		if ($update) {
153
-			$this->backend->updateCard($this->getKey(), $uri, $vCard->serialize());
154
-		} else {
155
-			$this->backend->createCard($this->getKey(), $uri, $vCard->serialize());
156
-		}
157
-
158
-		return $this->vCard2Array($uri, $vCard);
159
-	}
160
-
161
-	/**
162
-	 * @return mixed
163
-	 * @since 5.0.0
164
-	 */
165
-	public function getPermissions() {
166
-		$permissions = $this->addressBook->getACL();
167
-		$result = 0;
168
-		foreach ($permissions as $permission) {
169
-			switch ($permission['privilege']) {
170
-				case '{DAV:}read':
171
-					$result |= Constants::PERMISSION_READ;
172
-					break;
173
-				case '{DAV:}write':
174
-					$result |= Constants::PERMISSION_CREATE;
175
-					$result |= Constants::PERMISSION_UPDATE;
176
-					break;
177
-				case '{DAV:}all':
178
-					$result |= Constants::PERMISSION_ALL;
179
-					break;
180
-			}
181
-		}
182
-
183
-		return $result;
184
-	}
185
-
186
-	/**
187
-	 * @param object $id the unique identifier to a contact
188
-	 * @return bool successful or not
189
-	 * @since 5.0.0
190
-	 */
191
-	public function delete($id) {
192
-		$uri = $this->backend->getCardUri($id);
193
-		return $this->backend->deleteCard($this->addressBookInfo['id'], $uri);
194
-	}
195
-
196
-	/**
197
-	 * read vCard data into a vCard object
198
-	 *
199
-	 * @param string $cardData
200
-	 * @return VCard
201
-	 */
202
-	protected function readCard($cardData) {
203
-		return  Reader::read($cardData);
204
-	}
205
-
206
-	/**
207
-	 * create UID for contact
208
-	 *
209
-	 * @return string
210
-	 */
211
-	protected function createUid() {
212
-		do {
213
-			$uid = $this->getUid();
214
-			$contact = $this->backend->getContact($this->getKey(), $uid . '.vcf');
215
-		} while (!empty($contact));
216
-
217
-		return $uid;
218
-	}
219
-
220
-	/**
221
-	 * getUid is only there for testing, use createUid instead
222
-	 */
223
-	protected function getUid() {
224
-		return UUIDUtil::getUUID();
225
-	}
226
-
227
-	/**
228
-	 * create empty vcard
229
-	 *
230
-	 * @param string $uid
231
-	 * @return VCard
232
-	 */
233
-	protected function createEmptyVCard($uid) {
234
-		$vCard = new VCard();
235
-		$vCard->UID = $uid;
236
-		return $vCard;
237
-	}
238
-
239
-	/**
240
-	 * create array with all vCard properties
241
-	 *
242
-	 * @param string $uri
243
-	 * @param VCard $vCard
244
-	 * @param boolean $withTypes (optional) return the values as arrays of value/type pairs
245
-	 * @return array
246
-	 */
247
-	protected function vCard2Array($uri, VCard $vCard, $withTypes = false) {
248
-		$result = [
249
-			'URI' => $uri,
250
-		];
251
-
252
-		foreach ($vCard->children() as $property) {
253
-			if ($property->name === 'PHOTO' && $property->getValueType() === 'BINARY') {
254
-				$url = $this->urlGenerator->getAbsoluteURL(
255
-					$this->urlGenerator->linkTo('', 'remote.php') . '/dav/');
256
-				$url .= implode('/', [
257
-					'addressbooks',
258
-					substr($this->addressBookInfo['principaluri'], 11), //cut off 'principals/'
259
-					$this->addressBookInfo['uri'],
260
-					$uri
261
-				]) . '?photo';
262
-
263
-				$result['PHOTO'] = 'VALUE=uri:' . $url;
264
-			} elseif (in_array($property->name, ['URL', 'GEO', 'CLOUD', 'ADR', 'EMAIL', 'IMPP', 'TEL', 'X-SOCIALPROFILE', 'RELATED', 'LANG', 'X-ADDRESSBOOKSERVER-MEMBER'])) {
265
-				if (!isset($result[$property->name])) {
266
-					$result[$property->name] = [];
267
-				}
268
-
269
-				$type = $this->getTypeFromProperty($property);
270
-				if ($withTypes) {
271
-					$result[$property->name][] = [
272
-						'type' => $type,
273
-						'value' => $property->getValue()
274
-					];
275
-				} else {
276
-					$result[$property->name][] = $property->getValue();
277
-				}
278
-			} else {
279
-				$result[$property->name] = $property->getValue();
280
-			}
281
-		}
282
-
283
-		if ($this->isSystemAddressBook()) {
284
-			$result['isLocalSystemBook'] = true;
285
-		}
286
-		return $result;
287
-	}
288
-
289
-	/**
290
-	 * Get the type of the current property
291
-	 *
292
-	 * @param Property $property
293
-	 * @return null|string
294
-	 */
295
-	protected function getTypeFromProperty(Property $property) {
296
-		$parameters = $property->parameters();
297
-		// Type is the social network, when it's empty we don't need this.
298
-		if (isset($parameters['TYPE'])) {
299
-			/** @var \Sabre\VObject\Parameter $type */
300
-			$type = $parameters['TYPE'];
301
-			return $type->getValue();
302
-		}
303
-
304
-		return null;
305
-	}
306
-
307
-	/**
308
-	 * @inheritDoc
309
-	 */
310
-	public function isShared(): bool {
311
-		if (!isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
312
-			return false;
313
-		}
314
-
315
-		return $this->addressBookInfo['principaluri']
316
-			!== $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'];
317
-	}
318
-
319
-	/**
320
-	 * @inheritDoc
321
-	 */
322
-	public function isSystemAddressBook(): bool {
323
-		return $this->addressBookInfo['principaluri'] === 'principals/system/system' && (
324
-			$this->addressBookInfo['uri'] === 'system' ||
325
-			$this->addressBookInfo['{DAV:}displayname'] === $this->urlGenerator->getBaseUrl()
326
-		);
327
-	}
43
+    /** @var CardDavBackend */
44
+    private $backend;
45
+
46
+    /** @var array */
47
+    private $addressBookInfo;
48
+
49
+    /** @var AddressBook */
50
+    private $addressBook;
51
+
52
+    /** @var IURLGenerator */
53
+    private $urlGenerator;
54
+
55
+    /**
56
+     * AddressBookImpl constructor.
57
+     *
58
+     * @param AddressBook $addressBook
59
+     * @param array $addressBookInfo
60
+     * @param CardDavBackend $backend
61
+     * @param IUrlGenerator $urlGenerator
62
+     */
63
+    public function __construct(
64
+            AddressBook $addressBook,
65
+            array $addressBookInfo,
66
+            CardDavBackend $backend,
67
+            IURLGenerator $urlGenerator) {
68
+        $this->addressBook = $addressBook;
69
+        $this->addressBookInfo = $addressBookInfo;
70
+        $this->backend = $backend;
71
+        $this->urlGenerator = $urlGenerator;
72
+    }
73
+
74
+    /**
75
+     * @return string defining the technical unique key
76
+     * @since 5.0.0
77
+     */
78
+    public function getKey() {
79
+        return $this->addressBookInfo['id'];
80
+    }
81
+
82
+    /**
83
+     * @return string defining the unique uri
84
+     * @since 16.0.0
85
+     */
86
+    public function getUri(): string {
87
+        return $this->addressBookInfo['uri'];
88
+    }
89
+
90
+    /**
91
+     * In comparison to getKey() this function returns a human readable (maybe translated) name
92
+     *
93
+     * @return mixed
94
+     * @since 5.0.0
95
+     */
96
+    public function getDisplayName() {
97
+        return $this->addressBookInfo['{DAV:}displayname'];
98
+    }
99
+
100
+    /**
101
+     * @param string $pattern which should match within the $searchProperties
102
+     * @param array $searchProperties defines the properties within the query pattern should match
103
+     * @param array $options Options to define the output format and search behavior
104
+     * 	- 'types' boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
105
+     *    example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => '[email protected]']]
106
+     * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
107
+     * 	- 'limit' - Set a numeric limit for the search results
108
+     * 	- 'offset' - Set the offset for the limited search results
109
+     * @return array an array of contacts which are arrays of key-value-pairs
110
+     *  example result:
111
+     *  [
112
+     *		['id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => '[email protected]', 'GEO' => '37.386013;-122.082932'],
113
+     *		['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['[email protected]', '[email protected]']]
114
+     *	]
115
+     * @since 5.0.0
116
+     */
117
+    public function search($pattern, $searchProperties, $options) {
118
+        $results = $this->backend->search($this->getKey(), $pattern, $searchProperties, $options);
119
+
120
+        $withTypes = \array_key_exists('types', $options) && $options['types'] === true;
121
+
122
+        $vCards = [];
123
+        foreach ($results as $result) {
124
+            $vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']), $withTypes);
125
+        }
126
+
127
+        return $vCards;
128
+    }
129
+
130
+    /**
131
+     * @param array $properties this array if key-value-pairs defines a contact
132
+     * @return array an array representing the contact just created or updated
133
+     * @since 5.0.0
134
+     */
135
+    public function createOrUpdate($properties) {
136
+        $update = false;
137
+        if (!isset($properties['URI'])) { // create a new contact
138
+            $uid = $this->createUid();
139
+            $uri = $uid . '.vcf';
140
+            $vCard = $this->createEmptyVCard($uid);
141
+        } else { // update existing contact
142
+            $uri = $properties['URI'];
143
+            $vCardData = $this->backend->getCard($this->getKey(), $uri);
144
+            $vCard = $this->readCard($vCardData['carddata']);
145
+            $update = true;
146
+        }
147
+
148
+        foreach ($properties as $key => $value) {
149
+            $vCard->$key = $vCard->createProperty($key, $value);
150
+        }
151
+
152
+        if ($update) {
153
+            $this->backend->updateCard($this->getKey(), $uri, $vCard->serialize());
154
+        } else {
155
+            $this->backend->createCard($this->getKey(), $uri, $vCard->serialize());
156
+        }
157
+
158
+        return $this->vCard2Array($uri, $vCard);
159
+    }
160
+
161
+    /**
162
+     * @return mixed
163
+     * @since 5.0.0
164
+     */
165
+    public function getPermissions() {
166
+        $permissions = $this->addressBook->getACL();
167
+        $result = 0;
168
+        foreach ($permissions as $permission) {
169
+            switch ($permission['privilege']) {
170
+                case '{DAV:}read':
171
+                    $result |= Constants::PERMISSION_READ;
172
+                    break;
173
+                case '{DAV:}write':
174
+                    $result |= Constants::PERMISSION_CREATE;
175
+                    $result |= Constants::PERMISSION_UPDATE;
176
+                    break;
177
+                case '{DAV:}all':
178
+                    $result |= Constants::PERMISSION_ALL;
179
+                    break;
180
+            }
181
+        }
182
+
183
+        return $result;
184
+    }
185
+
186
+    /**
187
+     * @param object $id the unique identifier to a contact
188
+     * @return bool successful or not
189
+     * @since 5.0.0
190
+     */
191
+    public function delete($id) {
192
+        $uri = $this->backend->getCardUri($id);
193
+        return $this->backend->deleteCard($this->addressBookInfo['id'], $uri);
194
+    }
195
+
196
+    /**
197
+     * read vCard data into a vCard object
198
+     *
199
+     * @param string $cardData
200
+     * @return VCard
201
+     */
202
+    protected function readCard($cardData) {
203
+        return  Reader::read($cardData);
204
+    }
205
+
206
+    /**
207
+     * create UID for contact
208
+     *
209
+     * @return string
210
+     */
211
+    protected function createUid() {
212
+        do {
213
+            $uid = $this->getUid();
214
+            $contact = $this->backend->getContact($this->getKey(), $uid . '.vcf');
215
+        } while (!empty($contact));
216
+
217
+        return $uid;
218
+    }
219
+
220
+    /**
221
+     * getUid is only there for testing, use createUid instead
222
+     */
223
+    protected function getUid() {
224
+        return UUIDUtil::getUUID();
225
+    }
226
+
227
+    /**
228
+     * create empty vcard
229
+     *
230
+     * @param string $uid
231
+     * @return VCard
232
+     */
233
+    protected function createEmptyVCard($uid) {
234
+        $vCard = new VCard();
235
+        $vCard->UID = $uid;
236
+        return $vCard;
237
+    }
238
+
239
+    /**
240
+     * create array with all vCard properties
241
+     *
242
+     * @param string $uri
243
+     * @param VCard $vCard
244
+     * @param boolean $withTypes (optional) return the values as arrays of value/type pairs
245
+     * @return array
246
+     */
247
+    protected function vCard2Array($uri, VCard $vCard, $withTypes = false) {
248
+        $result = [
249
+            'URI' => $uri,
250
+        ];
251
+
252
+        foreach ($vCard->children() as $property) {
253
+            if ($property->name === 'PHOTO' && $property->getValueType() === 'BINARY') {
254
+                $url = $this->urlGenerator->getAbsoluteURL(
255
+                    $this->urlGenerator->linkTo('', 'remote.php') . '/dav/');
256
+                $url .= implode('/', [
257
+                    'addressbooks',
258
+                    substr($this->addressBookInfo['principaluri'], 11), //cut off 'principals/'
259
+                    $this->addressBookInfo['uri'],
260
+                    $uri
261
+                ]) . '?photo';
262
+
263
+                $result['PHOTO'] = 'VALUE=uri:' . $url;
264
+            } elseif (in_array($property->name, ['URL', 'GEO', 'CLOUD', 'ADR', 'EMAIL', 'IMPP', 'TEL', 'X-SOCIALPROFILE', 'RELATED', 'LANG', 'X-ADDRESSBOOKSERVER-MEMBER'])) {
265
+                if (!isset($result[$property->name])) {
266
+                    $result[$property->name] = [];
267
+                }
268
+
269
+                $type = $this->getTypeFromProperty($property);
270
+                if ($withTypes) {
271
+                    $result[$property->name][] = [
272
+                        'type' => $type,
273
+                        'value' => $property->getValue()
274
+                    ];
275
+                } else {
276
+                    $result[$property->name][] = $property->getValue();
277
+                }
278
+            } else {
279
+                $result[$property->name] = $property->getValue();
280
+            }
281
+        }
282
+
283
+        if ($this->isSystemAddressBook()) {
284
+            $result['isLocalSystemBook'] = true;
285
+        }
286
+        return $result;
287
+    }
288
+
289
+    /**
290
+     * Get the type of the current property
291
+     *
292
+     * @param Property $property
293
+     * @return null|string
294
+     */
295
+    protected function getTypeFromProperty(Property $property) {
296
+        $parameters = $property->parameters();
297
+        // Type is the social network, when it's empty we don't need this.
298
+        if (isset($parameters['TYPE'])) {
299
+            /** @var \Sabre\VObject\Parameter $type */
300
+            $type = $parameters['TYPE'];
301
+            return $type->getValue();
302
+        }
303
+
304
+        return null;
305
+    }
306
+
307
+    /**
308
+     * @inheritDoc
309
+     */
310
+    public function isShared(): bool {
311
+        if (!isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
312
+            return false;
313
+        }
314
+
315
+        return $this->addressBookInfo['principaluri']
316
+            !== $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'];
317
+    }
318
+
319
+    /**
320
+     * @inheritDoc
321
+     */
322
+    public function isSystemAddressBook(): bool {
323
+        return $this->addressBookInfo['principaluri'] === 'principals/system/system' && (
324
+            $this->addressBookInfo['uri'] === 'system' ||
325
+            $this->addressBookInfo['{DAV:}displayname'] === $this->urlGenerator->getBaseUrl()
326
+        );
327
+    }
328 328
 }
Please login to merge, or discard this patch.