@@ -53,989 +53,989 @@ |
||
53 | 53 | */ |
54 | 54 | class FederatedShareProvider implements IShareProvider { |
55 | 55 | |
56 | - const SHARE_TYPE_REMOTE = 6; |
|
57 | - |
|
58 | - /** @var IDBConnection */ |
|
59 | - private $dbConnection; |
|
60 | - |
|
61 | - /** @var AddressHandler */ |
|
62 | - private $addressHandler; |
|
63 | - |
|
64 | - /** @var Notifications */ |
|
65 | - private $notifications; |
|
66 | - |
|
67 | - /** @var TokenHandler */ |
|
68 | - private $tokenHandler; |
|
69 | - |
|
70 | - /** @var IL10N */ |
|
71 | - private $l; |
|
72 | - |
|
73 | - /** @var ILogger */ |
|
74 | - private $logger; |
|
75 | - |
|
76 | - /** @var IRootFolder */ |
|
77 | - private $rootFolder; |
|
78 | - |
|
79 | - /** @var IConfig */ |
|
80 | - private $config; |
|
81 | - |
|
82 | - /** @var string */ |
|
83 | - private $externalShareTable = 'share_external'; |
|
84 | - |
|
85 | - /** @var IUserManager */ |
|
86 | - private $userManager; |
|
87 | - |
|
88 | - /** @var ICloudIdManager */ |
|
89 | - private $cloudIdManager; |
|
90 | - |
|
91 | - /** @var \OCP\GlobalScale\IConfig */ |
|
92 | - private $gsConfig; |
|
93 | - |
|
94 | - /** |
|
95 | - * DefaultShareProvider constructor. |
|
96 | - * |
|
97 | - * @param IDBConnection $connection |
|
98 | - * @param AddressHandler $addressHandler |
|
99 | - * @param Notifications $notifications |
|
100 | - * @param TokenHandler $tokenHandler |
|
101 | - * @param IL10N $l10n |
|
102 | - * @param ILogger $logger |
|
103 | - * @param IRootFolder $rootFolder |
|
104 | - * @param IConfig $config |
|
105 | - * @param IUserManager $userManager |
|
106 | - * @param ICloudIdManager $cloudIdManager |
|
107 | - * @param \OCP\GlobalScale\IConfig $globalScaleConfig |
|
108 | - */ |
|
109 | - public function __construct( |
|
110 | - IDBConnection $connection, |
|
111 | - AddressHandler $addressHandler, |
|
112 | - Notifications $notifications, |
|
113 | - TokenHandler $tokenHandler, |
|
114 | - IL10N $l10n, |
|
115 | - ILogger $logger, |
|
116 | - IRootFolder $rootFolder, |
|
117 | - IConfig $config, |
|
118 | - IUserManager $userManager, |
|
119 | - ICloudIdManager $cloudIdManager, |
|
120 | - \OCP\GlobalScale\IConfig $globalScaleConfig |
|
121 | - ) { |
|
122 | - $this->dbConnection = $connection; |
|
123 | - $this->addressHandler = $addressHandler; |
|
124 | - $this->notifications = $notifications; |
|
125 | - $this->tokenHandler = $tokenHandler; |
|
126 | - $this->l = $l10n; |
|
127 | - $this->logger = $logger; |
|
128 | - $this->rootFolder = $rootFolder; |
|
129 | - $this->config = $config; |
|
130 | - $this->userManager = $userManager; |
|
131 | - $this->cloudIdManager = $cloudIdManager; |
|
132 | - $this->gsConfig = $globalScaleConfig; |
|
133 | - } |
|
134 | - |
|
135 | - /** |
|
136 | - * Return the identifier of this provider. |
|
137 | - * |
|
138 | - * @return string Containing only [a-zA-Z0-9] |
|
139 | - */ |
|
140 | - public function identifier() { |
|
141 | - return 'ocFederatedSharing'; |
|
142 | - } |
|
143 | - |
|
144 | - /** |
|
145 | - * Share a path |
|
146 | - * |
|
147 | - * @param IShare $share |
|
148 | - * @return IShare The share object |
|
149 | - * @throws ShareNotFound |
|
150 | - * @throws \Exception |
|
151 | - */ |
|
152 | - public function create(IShare $share) { |
|
153 | - |
|
154 | - $shareWith = $share->getSharedWith(); |
|
155 | - $itemSource = $share->getNodeId(); |
|
156 | - $itemType = $share->getNodeType(); |
|
157 | - $permissions = $share->getPermissions(); |
|
158 | - $sharedBy = $share->getSharedBy(); |
|
159 | - |
|
160 | - /* |
|
56 | + const SHARE_TYPE_REMOTE = 6; |
|
57 | + |
|
58 | + /** @var IDBConnection */ |
|
59 | + private $dbConnection; |
|
60 | + |
|
61 | + /** @var AddressHandler */ |
|
62 | + private $addressHandler; |
|
63 | + |
|
64 | + /** @var Notifications */ |
|
65 | + private $notifications; |
|
66 | + |
|
67 | + /** @var TokenHandler */ |
|
68 | + private $tokenHandler; |
|
69 | + |
|
70 | + /** @var IL10N */ |
|
71 | + private $l; |
|
72 | + |
|
73 | + /** @var ILogger */ |
|
74 | + private $logger; |
|
75 | + |
|
76 | + /** @var IRootFolder */ |
|
77 | + private $rootFolder; |
|
78 | + |
|
79 | + /** @var IConfig */ |
|
80 | + private $config; |
|
81 | + |
|
82 | + /** @var string */ |
|
83 | + private $externalShareTable = 'share_external'; |
|
84 | + |
|
85 | + /** @var IUserManager */ |
|
86 | + private $userManager; |
|
87 | + |
|
88 | + /** @var ICloudIdManager */ |
|
89 | + private $cloudIdManager; |
|
90 | + |
|
91 | + /** @var \OCP\GlobalScale\IConfig */ |
|
92 | + private $gsConfig; |
|
93 | + |
|
94 | + /** |
|
95 | + * DefaultShareProvider constructor. |
|
96 | + * |
|
97 | + * @param IDBConnection $connection |
|
98 | + * @param AddressHandler $addressHandler |
|
99 | + * @param Notifications $notifications |
|
100 | + * @param TokenHandler $tokenHandler |
|
101 | + * @param IL10N $l10n |
|
102 | + * @param ILogger $logger |
|
103 | + * @param IRootFolder $rootFolder |
|
104 | + * @param IConfig $config |
|
105 | + * @param IUserManager $userManager |
|
106 | + * @param ICloudIdManager $cloudIdManager |
|
107 | + * @param \OCP\GlobalScale\IConfig $globalScaleConfig |
|
108 | + */ |
|
109 | + public function __construct( |
|
110 | + IDBConnection $connection, |
|
111 | + AddressHandler $addressHandler, |
|
112 | + Notifications $notifications, |
|
113 | + TokenHandler $tokenHandler, |
|
114 | + IL10N $l10n, |
|
115 | + ILogger $logger, |
|
116 | + IRootFolder $rootFolder, |
|
117 | + IConfig $config, |
|
118 | + IUserManager $userManager, |
|
119 | + ICloudIdManager $cloudIdManager, |
|
120 | + \OCP\GlobalScale\IConfig $globalScaleConfig |
|
121 | + ) { |
|
122 | + $this->dbConnection = $connection; |
|
123 | + $this->addressHandler = $addressHandler; |
|
124 | + $this->notifications = $notifications; |
|
125 | + $this->tokenHandler = $tokenHandler; |
|
126 | + $this->l = $l10n; |
|
127 | + $this->logger = $logger; |
|
128 | + $this->rootFolder = $rootFolder; |
|
129 | + $this->config = $config; |
|
130 | + $this->userManager = $userManager; |
|
131 | + $this->cloudIdManager = $cloudIdManager; |
|
132 | + $this->gsConfig = $globalScaleConfig; |
|
133 | + } |
|
134 | + |
|
135 | + /** |
|
136 | + * Return the identifier of this provider. |
|
137 | + * |
|
138 | + * @return string Containing only [a-zA-Z0-9] |
|
139 | + */ |
|
140 | + public function identifier() { |
|
141 | + return 'ocFederatedSharing'; |
|
142 | + } |
|
143 | + |
|
144 | + /** |
|
145 | + * Share a path |
|
146 | + * |
|
147 | + * @param IShare $share |
|
148 | + * @return IShare The share object |
|
149 | + * @throws ShareNotFound |
|
150 | + * @throws \Exception |
|
151 | + */ |
|
152 | + public function create(IShare $share) { |
|
153 | + |
|
154 | + $shareWith = $share->getSharedWith(); |
|
155 | + $itemSource = $share->getNodeId(); |
|
156 | + $itemType = $share->getNodeType(); |
|
157 | + $permissions = $share->getPermissions(); |
|
158 | + $sharedBy = $share->getSharedBy(); |
|
159 | + |
|
160 | + /* |
|
161 | 161 | * Check if file is not already shared with the remote user |
162 | 162 | */ |
163 | - $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0); |
|
164 | - if (!empty($alreadyShared)) { |
|
165 | - $message = 'Sharing %s failed, because this item is already shared with %s'; |
|
166 | - $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
167 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
168 | - throw new \Exception($message_t); |
|
169 | - } |
|
170 | - |
|
171 | - |
|
172 | - // don't allow federated shares if source and target server are the same |
|
173 | - $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
174 | - $currentServer = $this->addressHandler->generateRemoteURL(); |
|
175 | - $currentUser = $sharedBy; |
|
176 | - if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
177 | - $message = 'Not allowed to create a federated share with the same user.'; |
|
178 | - $message_t = $this->l->t('Not allowed to create a federated share with the same user'); |
|
179 | - $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
180 | - throw new \Exception($message_t); |
|
181 | - } |
|
182 | - |
|
183 | - |
|
184 | - $share->setSharedWith($cloudId->getId()); |
|
185 | - |
|
186 | - try { |
|
187 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
188 | - } catch (ShareNotFound $e) { |
|
189 | - $remoteShare = null; |
|
190 | - } |
|
191 | - |
|
192 | - if ($remoteShare) { |
|
193 | - try { |
|
194 | - $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
195 | - $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time()); |
|
196 | - $share->setId($shareId); |
|
197 | - list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
198 | - // remote share was create successfully if we get a valid token as return |
|
199 | - $send = is_string($token) && $token !== ''; |
|
200 | - } catch (\Exception $e) { |
|
201 | - // fall back to old re-share behavior if the remote server |
|
202 | - // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
203 | - $this->removeShareFromTable($share); |
|
204 | - $shareId = $this->createFederatedShare($share); |
|
205 | - } |
|
206 | - if ($send) { |
|
207 | - $this->updateSuccessfulReshare($shareId, $token); |
|
208 | - $this->storeRemoteId($shareId, $remoteId); |
|
209 | - } else { |
|
210 | - $this->removeShareFromTable($share); |
|
211 | - $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
212 | - throw new \Exception($message_t); |
|
213 | - } |
|
214 | - |
|
215 | - } else { |
|
216 | - $shareId = $this->createFederatedShare($share); |
|
217 | - } |
|
218 | - |
|
219 | - $data = $this->getRawShare($shareId); |
|
220 | - return $this->createShareObject($data); |
|
221 | - } |
|
222 | - |
|
223 | - /** |
|
224 | - * create federated share and inform the recipient |
|
225 | - * |
|
226 | - * @param IShare $share |
|
227 | - * @return int |
|
228 | - * @throws ShareNotFound |
|
229 | - * @throws \Exception |
|
230 | - */ |
|
231 | - protected function createFederatedShare(IShare $share) { |
|
232 | - $token = $this->tokenHandler->generateToken(); |
|
233 | - $shareId = $this->addShareToDB( |
|
234 | - $share->getNodeId(), |
|
235 | - $share->getNodeType(), |
|
236 | - $share->getSharedWith(), |
|
237 | - $share->getSharedBy(), |
|
238 | - $share->getShareOwner(), |
|
239 | - $share->getPermissions(), |
|
240 | - $token |
|
241 | - ); |
|
242 | - |
|
243 | - $failure = false; |
|
244 | - |
|
245 | - try { |
|
246 | - $sharedByFederatedId = $share->getSharedBy(); |
|
247 | - if ($this->userManager->userExists($sharedByFederatedId)) { |
|
248 | - $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
249 | - $sharedByFederatedId = $cloudId->getId(); |
|
250 | - } |
|
251 | - $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
252 | - $send = $this->notifications->sendRemoteShare( |
|
253 | - $token, |
|
254 | - $share->getSharedWith(), |
|
255 | - $share->getNode()->getName(), |
|
256 | - $shareId, |
|
257 | - $share->getShareOwner(), |
|
258 | - $ownerCloudId->getId(), |
|
259 | - $share->getSharedBy(), |
|
260 | - $sharedByFederatedId |
|
261 | - ); |
|
262 | - |
|
263 | - if ($send === false) { |
|
264 | - $failure = true; |
|
265 | - } |
|
266 | - } catch (\Exception $e) { |
|
267 | - $this->logger->logException($e, [ |
|
268 | - 'message' => 'Failed to notify remote server of federated share, removing share.', |
|
269 | - 'level' => ILogger::ERROR, |
|
270 | - 'app' => 'federatedfilesharing', |
|
271 | - ]); |
|
272 | - $failure = true; |
|
273 | - } |
|
274 | - |
|
275 | - if($failure) { |
|
276 | - $this->removeShareFromTableById($shareId); |
|
277 | - $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
278 | - [$share->getNode()->getName(), $share->getSharedWith()]); |
|
279 | - throw new \Exception($message_t); |
|
280 | - } |
|
281 | - |
|
282 | - return $shareId; |
|
283 | - |
|
284 | - } |
|
285 | - |
|
286 | - /** |
|
287 | - * @param string $shareWith |
|
288 | - * @param IShare $share |
|
289 | - * @param string $shareId internal share Id |
|
290 | - * @return array |
|
291 | - * @throws \Exception |
|
292 | - */ |
|
293 | - protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
294 | - |
|
295 | - $remoteShare = $this->getShareFromExternalShareTable($share); |
|
296 | - $token = $remoteShare['share_token']; |
|
297 | - $remoteId = $remoteShare['remote_id']; |
|
298 | - $remote = $remoteShare['remote']; |
|
299 | - |
|
300 | - list($token, $remoteId) = $this->notifications->requestReShare( |
|
301 | - $token, |
|
302 | - $remoteId, |
|
303 | - $shareId, |
|
304 | - $remote, |
|
305 | - $shareWith, |
|
306 | - $share->getPermissions() |
|
307 | - ); |
|
308 | - |
|
309 | - return [$token, $remoteId]; |
|
310 | - } |
|
311 | - |
|
312 | - /** |
|
313 | - * get federated share from the share_external table but exclude mounted link shares |
|
314 | - * |
|
315 | - * @param IShare $share |
|
316 | - * @return array |
|
317 | - * @throws ShareNotFound |
|
318 | - */ |
|
319 | - protected function getShareFromExternalShareTable(IShare $share) { |
|
320 | - $query = $this->dbConnection->getQueryBuilder(); |
|
321 | - $query->select('*')->from($this->externalShareTable) |
|
322 | - ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
323 | - ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
324 | - $result = $query->execute()->fetchAll(); |
|
325 | - |
|
326 | - if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
327 | - return $result[0]; |
|
328 | - } |
|
329 | - |
|
330 | - throw new ShareNotFound('share not found in share_external table'); |
|
331 | - } |
|
332 | - |
|
333 | - /** |
|
334 | - * add share to the database and return the ID |
|
335 | - * |
|
336 | - * @param int $itemSource |
|
337 | - * @param string $itemType |
|
338 | - * @param string $shareWith |
|
339 | - * @param string $sharedBy |
|
340 | - * @param string $uidOwner |
|
341 | - * @param int $permissions |
|
342 | - * @param string $token |
|
343 | - * @return int |
|
344 | - */ |
|
345 | - private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { |
|
346 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
347 | - $qb->insert('share') |
|
348 | - ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)) |
|
349 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
350 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
351 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
352 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
353 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
354 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
355 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
356 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
357 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
358 | - |
|
359 | - /* |
|
163 | + $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0); |
|
164 | + if (!empty($alreadyShared)) { |
|
165 | + $message = 'Sharing %s failed, because this item is already shared with %s'; |
|
166 | + $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
167 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
168 | + throw new \Exception($message_t); |
|
169 | + } |
|
170 | + |
|
171 | + |
|
172 | + // don't allow federated shares if source and target server are the same |
|
173 | + $cloudId = $this->cloudIdManager->resolveCloudId($shareWith); |
|
174 | + $currentServer = $this->addressHandler->generateRemoteURL(); |
|
175 | + $currentUser = $sharedBy; |
|
176 | + if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) { |
|
177 | + $message = 'Not allowed to create a federated share with the same user.'; |
|
178 | + $message_t = $this->l->t('Not allowed to create a federated share with the same user'); |
|
179 | + $this->logger->debug($message, ['app' => 'Federated File Sharing']); |
|
180 | + throw new \Exception($message_t); |
|
181 | + } |
|
182 | + |
|
183 | + |
|
184 | + $share->setSharedWith($cloudId->getId()); |
|
185 | + |
|
186 | + try { |
|
187 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
188 | + } catch (ShareNotFound $e) { |
|
189 | + $remoteShare = null; |
|
190 | + } |
|
191 | + |
|
192 | + if ($remoteShare) { |
|
193 | + try { |
|
194 | + $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); |
|
195 | + $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time()); |
|
196 | + $share->setId($shareId); |
|
197 | + list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId); |
|
198 | + // remote share was create successfully if we get a valid token as return |
|
199 | + $send = is_string($token) && $token !== ''; |
|
200 | + } catch (\Exception $e) { |
|
201 | + // fall back to old re-share behavior if the remote server |
|
202 | + // doesn't support flat re-shares (was introduced with Nextcloud 9.1) |
|
203 | + $this->removeShareFromTable($share); |
|
204 | + $shareId = $this->createFederatedShare($share); |
|
205 | + } |
|
206 | + if ($send) { |
|
207 | + $this->updateSuccessfulReshare($shareId, $token); |
|
208 | + $this->storeRemoteId($shareId, $remoteId); |
|
209 | + } else { |
|
210 | + $this->removeShareFromTable($share); |
|
211 | + $message_t = $this->l->t('File is already shared with %s', [$shareWith]); |
|
212 | + throw new \Exception($message_t); |
|
213 | + } |
|
214 | + |
|
215 | + } else { |
|
216 | + $shareId = $this->createFederatedShare($share); |
|
217 | + } |
|
218 | + |
|
219 | + $data = $this->getRawShare($shareId); |
|
220 | + return $this->createShareObject($data); |
|
221 | + } |
|
222 | + |
|
223 | + /** |
|
224 | + * create federated share and inform the recipient |
|
225 | + * |
|
226 | + * @param IShare $share |
|
227 | + * @return int |
|
228 | + * @throws ShareNotFound |
|
229 | + * @throws \Exception |
|
230 | + */ |
|
231 | + protected function createFederatedShare(IShare $share) { |
|
232 | + $token = $this->tokenHandler->generateToken(); |
|
233 | + $shareId = $this->addShareToDB( |
|
234 | + $share->getNodeId(), |
|
235 | + $share->getNodeType(), |
|
236 | + $share->getSharedWith(), |
|
237 | + $share->getSharedBy(), |
|
238 | + $share->getShareOwner(), |
|
239 | + $share->getPermissions(), |
|
240 | + $token |
|
241 | + ); |
|
242 | + |
|
243 | + $failure = false; |
|
244 | + |
|
245 | + try { |
|
246 | + $sharedByFederatedId = $share->getSharedBy(); |
|
247 | + if ($this->userManager->userExists($sharedByFederatedId)) { |
|
248 | + $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL()); |
|
249 | + $sharedByFederatedId = $cloudId->getId(); |
|
250 | + } |
|
251 | + $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL()); |
|
252 | + $send = $this->notifications->sendRemoteShare( |
|
253 | + $token, |
|
254 | + $share->getSharedWith(), |
|
255 | + $share->getNode()->getName(), |
|
256 | + $shareId, |
|
257 | + $share->getShareOwner(), |
|
258 | + $ownerCloudId->getId(), |
|
259 | + $share->getSharedBy(), |
|
260 | + $sharedByFederatedId |
|
261 | + ); |
|
262 | + |
|
263 | + if ($send === false) { |
|
264 | + $failure = true; |
|
265 | + } |
|
266 | + } catch (\Exception $e) { |
|
267 | + $this->logger->logException($e, [ |
|
268 | + 'message' => 'Failed to notify remote server of federated share, removing share.', |
|
269 | + 'level' => ILogger::ERROR, |
|
270 | + 'app' => 'federatedfilesharing', |
|
271 | + ]); |
|
272 | + $failure = true; |
|
273 | + } |
|
274 | + |
|
275 | + if($failure) { |
|
276 | + $this->removeShareFromTableById($shareId); |
|
277 | + $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.', |
|
278 | + [$share->getNode()->getName(), $share->getSharedWith()]); |
|
279 | + throw new \Exception($message_t); |
|
280 | + } |
|
281 | + |
|
282 | + return $shareId; |
|
283 | + |
|
284 | + } |
|
285 | + |
|
286 | + /** |
|
287 | + * @param string $shareWith |
|
288 | + * @param IShare $share |
|
289 | + * @param string $shareId internal share Id |
|
290 | + * @return array |
|
291 | + * @throws \Exception |
|
292 | + */ |
|
293 | + protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { |
|
294 | + |
|
295 | + $remoteShare = $this->getShareFromExternalShareTable($share); |
|
296 | + $token = $remoteShare['share_token']; |
|
297 | + $remoteId = $remoteShare['remote_id']; |
|
298 | + $remote = $remoteShare['remote']; |
|
299 | + |
|
300 | + list($token, $remoteId) = $this->notifications->requestReShare( |
|
301 | + $token, |
|
302 | + $remoteId, |
|
303 | + $shareId, |
|
304 | + $remote, |
|
305 | + $shareWith, |
|
306 | + $share->getPermissions() |
|
307 | + ); |
|
308 | + |
|
309 | + return [$token, $remoteId]; |
|
310 | + } |
|
311 | + |
|
312 | + /** |
|
313 | + * get federated share from the share_external table but exclude mounted link shares |
|
314 | + * |
|
315 | + * @param IShare $share |
|
316 | + * @return array |
|
317 | + * @throws ShareNotFound |
|
318 | + */ |
|
319 | + protected function getShareFromExternalShareTable(IShare $share) { |
|
320 | + $query = $this->dbConnection->getQueryBuilder(); |
|
321 | + $query->select('*')->from($this->externalShareTable) |
|
322 | + ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) |
|
323 | + ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); |
|
324 | + $result = $query->execute()->fetchAll(); |
|
325 | + |
|
326 | + if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) { |
|
327 | + return $result[0]; |
|
328 | + } |
|
329 | + |
|
330 | + throw new ShareNotFound('share not found in share_external table'); |
|
331 | + } |
|
332 | + |
|
333 | + /** |
|
334 | + * add share to the database and return the ID |
|
335 | + * |
|
336 | + * @param int $itemSource |
|
337 | + * @param string $itemType |
|
338 | + * @param string $shareWith |
|
339 | + * @param string $sharedBy |
|
340 | + * @param string $uidOwner |
|
341 | + * @param int $permissions |
|
342 | + * @param string $token |
|
343 | + * @return int |
|
344 | + */ |
|
345 | + private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { |
|
346 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
347 | + $qb->insert('share') |
|
348 | + ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)) |
|
349 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
350 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
351 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
352 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
353 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
354 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
355 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
356 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
357 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
358 | + |
|
359 | + /* |
|
360 | 360 | * Added to fix https://github.com/owncloud/core/issues/22215 |
361 | 361 | * Can be removed once we get rid of ajax/share.php |
362 | 362 | */ |
363 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
364 | - |
|
365 | - $qb->execute(); |
|
366 | - $id = $qb->getLastInsertId(); |
|
367 | - |
|
368 | - return (int)$id; |
|
369 | - } |
|
370 | - |
|
371 | - /** |
|
372 | - * Update a share |
|
373 | - * |
|
374 | - * @param IShare $share |
|
375 | - * @return IShare The share object |
|
376 | - */ |
|
377 | - public function update(IShare $share) { |
|
378 | - /* |
|
363 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
364 | + |
|
365 | + $qb->execute(); |
|
366 | + $id = $qb->getLastInsertId(); |
|
367 | + |
|
368 | + return (int)$id; |
|
369 | + } |
|
370 | + |
|
371 | + /** |
|
372 | + * Update a share |
|
373 | + * |
|
374 | + * @param IShare $share |
|
375 | + * @return IShare The share object |
|
376 | + */ |
|
377 | + public function update(IShare $share) { |
|
378 | + /* |
|
379 | 379 | * We allow updating the permissions of federated shares |
380 | 380 | */ |
381 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
382 | - $qb->update('share') |
|
383 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
384 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
385 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
386 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
387 | - ->execute(); |
|
388 | - |
|
389 | - // send the updated permission to the owner/initiator, if they are not the same |
|
390 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
391 | - $this->sendPermissionUpdate($share); |
|
392 | - } |
|
393 | - |
|
394 | - return $share; |
|
395 | - } |
|
396 | - |
|
397 | - /** |
|
398 | - * send the updated permission to the owner/initiator, if they are not the same |
|
399 | - * |
|
400 | - * @param IShare $share |
|
401 | - * @throws ShareNotFound |
|
402 | - * @throws \OC\HintException |
|
403 | - */ |
|
404 | - protected function sendPermissionUpdate(IShare $share) { |
|
405 | - $remoteId = $this->getRemoteId($share); |
|
406 | - // if the local user is the owner we send the permission change to the initiator |
|
407 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
408 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
409 | - } else { // ... if not we send the permission change to the owner |
|
410 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
411 | - } |
|
412 | - $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
413 | - } |
|
414 | - |
|
415 | - |
|
416 | - /** |
|
417 | - * update successful reShare with the correct token |
|
418 | - * |
|
419 | - * @param int $shareId |
|
420 | - * @param string $token |
|
421 | - */ |
|
422 | - protected function updateSuccessfulReShare($shareId, $token) { |
|
423 | - $query = $this->dbConnection->getQueryBuilder(); |
|
424 | - $query->update('share') |
|
425 | - ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
426 | - ->set('token', $query->createNamedParameter($token)) |
|
427 | - ->execute(); |
|
428 | - } |
|
429 | - |
|
430 | - /** |
|
431 | - * store remote ID in federated reShare table |
|
432 | - * |
|
433 | - * @param $shareId |
|
434 | - * @param $remoteId |
|
435 | - */ |
|
436 | - public function storeRemoteId($shareId, $remoteId) { |
|
437 | - $query = $this->dbConnection->getQueryBuilder(); |
|
438 | - $query->insert('federated_reshares') |
|
439 | - ->values( |
|
440 | - [ |
|
441 | - 'share_id' => $query->createNamedParameter($shareId), |
|
442 | - 'remote_id' => $query->createNamedParameter($remoteId), |
|
443 | - ] |
|
444 | - ); |
|
445 | - $query->execute(); |
|
446 | - } |
|
447 | - |
|
448 | - /** |
|
449 | - * get share ID on remote server for federated re-shares |
|
450 | - * |
|
451 | - * @param IShare $share |
|
452 | - * @return int |
|
453 | - * @throws ShareNotFound |
|
454 | - */ |
|
455 | - public function getRemoteId(IShare $share) { |
|
456 | - $query = $this->dbConnection->getQueryBuilder(); |
|
457 | - $query->select('remote_id')->from('federated_reshares') |
|
458 | - ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
459 | - $data = $query->execute()->fetch(); |
|
460 | - |
|
461 | - if (!is_array($data) || !isset($data['remote_id'])) { |
|
462 | - throw new ShareNotFound(); |
|
463 | - } |
|
464 | - |
|
465 | - return (int)$data['remote_id']; |
|
466 | - } |
|
467 | - |
|
468 | - /** |
|
469 | - * @inheritdoc |
|
470 | - */ |
|
471 | - public function move(IShare $share, $recipient) { |
|
472 | - /* |
|
381 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
382 | + $qb->update('share') |
|
383 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
384 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
385 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
386 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
387 | + ->execute(); |
|
388 | + |
|
389 | + // send the updated permission to the owner/initiator, if they are not the same |
|
390 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
391 | + $this->sendPermissionUpdate($share); |
|
392 | + } |
|
393 | + |
|
394 | + return $share; |
|
395 | + } |
|
396 | + |
|
397 | + /** |
|
398 | + * send the updated permission to the owner/initiator, if they are not the same |
|
399 | + * |
|
400 | + * @param IShare $share |
|
401 | + * @throws ShareNotFound |
|
402 | + * @throws \OC\HintException |
|
403 | + */ |
|
404 | + protected function sendPermissionUpdate(IShare $share) { |
|
405 | + $remoteId = $this->getRemoteId($share); |
|
406 | + // if the local user is the owner we send the permission change to the initiator |
|
407 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
408 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
409 | + } else { // ... if not we send the permission change to the owner |
|
410 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
411 | + } |
|
412 | + $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions()); |
|
413 | + } |
|
414 | + |
|
415 | + |
|
416 | + /** |
|
417 | + * update successful reShare with the correct token |
|
418 | + * |
|
419 | + * @param int $shareId |
|
420 | + * @param string $token |
|
421 | + */ |
|
422 | + protected function updateSuccessfulReShare($shareId, $token) { |
|
423 | + $query = $this->dbConnection->getQueryBuilder(); |
|
424 | + $query->update('share') |
|
425 | + ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) |
|
426 | + ->set('token', $query->createNamedParameter($token)) |
|
427 | + ->execute(); |
|
428 | + } |
|
429 | + |
|
430 | + /** |
|
431 | + * store remote ID in federated reShare table |
|
432 | + * |
|
433 | + * @param $shareId |
|
434 | + * @param $remoteId |
|
435 | + */ |
|
436 | + public function storeRemoteId($shareId, $remoteId) { |
|
437 | + $query = $this->dbConnection->getQueryBuilder(); |
|
438 | + $query->insert('federated_reshares') |
|
439 | + ->values( |
|
440 | + [ |
|
441 | + 'share_id' => $query->createNamedParameter($shareId), |
|
442 | + 'remote_id' => $query->createNamedParameter($remoteId), |
|
443 | + ] |
|
444 | + ); |
|
445 | + $query->execute(); |
|
446 | + } |
|
447 | + |
|
448 | + /** |
|
449 | + * get share ID on remote server for federated re-shares |
|
450 | + * |
|
451 | + * @param IShare $share |
|
452 | + * @return int |
|
453 | + * @throws ShareNotFound |
|
454 | + */ |
|
455 | + public function getRemoteId(IShare $share) { |
|
456 | + $query = $this->dbConnection->getQueryBuilder(); |
|
457 | + $query->select('remote_id')->from('federated_reshares') |
|
458 | + ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); |
|
459 | + $data = $query->execute()->fetch(); |
|
460 | + |
|
461 | + if (!is_array($data) || !isset($data['remote_id'])) { |
|
462 | + throw new ShareNotFound(); |
|
463 | + } |
|
464 | + |
|
465 | + return (int)$data['remote_id']; |
|
466 | + } |
|
467 | + |
|
468 | + /** |
|
469 | + * @inheritdoc |
|
470 | + */ |
|
471 | + public function move(IShare $share, $recipient) { |
|
472 | + /* |
|
473 | 473 | * This function does nothing yet as it is just for outgoing |
474 | 474 | * federated shares. |
475 | 475 | */ |
476 | - return $share; |
|
477 | - } |
|
478 | - |
|
479 | - /** |
|
480 | - * Get all children of this share |
|
481 | - * |
|
482 | - * @param IShare $parent |
|
483 | - * @return IShare[] |
|
484 | - */ |
|
485 | - public function getChildren(IShare $parent) { |
|
486 | - $children = []; |
|
487 | - |
|
488 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
489 | - $qb->select('*') |
|
490 | - ->from('share') |
|
491 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
492 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
493 | - ->orderBy('id'); |
|
494 | - |
|
495 | - $cursor = $qb->execute(); |
|
496 | - while($data = $cursor->fetch()) { |
|
497 | - $children[] = $this->createShareObject($data); |
|
498 | - } |
|
499 | - $cursor->closeCursor(); |
|
500 | - |
|
501 | - return $children; |
|
502 | - } |
|
503 | - |
|
504 | - /** |
|
505 | - * Delete a share (owner unShares the file) |
|
506 | - * |
|
507 | - * @param IShare $share |
|
508 | - */ |
|
509 | - public function delete(IShare $share) { |
|
510 | - |
|
511 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
512 | - |
|
513 | - $isOwner = false; |
|
514 | - |
|
515 | - $this->removeShareFromTable($share); |
|
516 | - |
|
517 | - // if the local user is the owner we can send the unShare request directly... |
|
518 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
519 | - $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
520 | - $this->revokeShare($share, true); |
|
521 | - $isOwner = true; |
|
522 | - } else { // ... if not we need to correct ID for the unShare request |
|
523 | - $remoteId = $this->getRemoteId($share); |
|
524 | - $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
525 | - $this->revokeShare($share, false); |
|
526 | - } |
|
527 | - |
|
528 | - // send revoke notification to the other user, if initiator and owner are not the same user |
|
529 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
530 | - $remoteId = $this->getRemoteId($share); |
|
531 | - if ($isOwner) { |
|
532 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
533 | - } else { |
|
534 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
535 | - } |
|
536 | - $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
537 | - } |
|
538 | - } |
|
539 | - |
|
540 | - /** |
|
541 | - * in case of a re-share we need to send the other use (initiator or owner) |
|
542 | - * a message that the file was unshared |
|
543 | - * |
|
544 | - * @param IShare $share |
|
545 | - * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
546 | - * @throws ShareNotFound |
|
547 | - * @throws \OC\HintException |
|
548 | - */ |
|
549 | - protected function revokeShare($share, $isOwner) { |
|
550 | - // also send a unShare request to the initiator, if this is a different user than the owner |
|
551 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
552 | - if ($isOwner) { |
|
553 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
554 | - } else { |
|
555 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
556 | - } |
|
557 | - $remoteId = $this->getRemoteId($share); |
|
558 | - $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
559 | - } |
|
560 | - } |
|
561 | - |
|
562 | - /** |
|
563 | - * remove share from table |
|
564 | - * |
|
565 | - * @param IShare $share |
|
566 | - */ |
|
567 | - public function removeShareFromTable(IShare $share) { |
|
568 | - $this->removeShareFromTableById($share->getId()); |
|
569 | - } |
|
570 | - |
|
571 | - /** |
|
572 | - * remove share from table |
|
573 | - * |
|
574 | - * @param string $shareId |
|
575 | - */ |
|
576 | - private function removeShareFromTableById($shareId) { |
|
577 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
578 | - $qb->delete('share') |
|
579 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
580 | - $qb->execute(); |
|
581 | - |
|
582 | - $qb->delete('federated_reshares') |
|
583 | - ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
584 | - $qb->execute(); |
|
585 | - } |
|
586 | - |
|
587 | - /** |
|
588 | - * @inheritdoc |
|
589 | - */ |
|
590 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
591 | - // nothing to do here. Technically deleteFromSelf in the context of federated |
|
592 | - // shares is a umount of a external storage. This is handled here |
|
593 | - // apps/files_sharing/lib/external/manager.php |
|
594 | - // TODO move this code over to this app |
|
595 | - } |
|
596 | - |
|
597 | - |
|
598 | - public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
599 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
600 | - $qb->select('*') |
|
601 | - ->from('share', 's') |
|
602 | - ->andWhere($qb->expr()->orX( |
|
603 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
604 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
605 | - )) |
|
606 | - ->andWhere( |
|
607 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)) |
|
608 | - ); |
|
609 | - |
|
610 | - /** |
|
611 | - * Reshares for this user are shares where they are the owner. |
|
612 | - */ |
|
613 | - if ($reshares === false) { |
|
614 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
615 | - } else { |
|
616 | - $qb->andWhere( |
|
617 | - $qb->expr()->orX( |
|
618 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
619 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
620 | - ) |
|
621 | - ); |
|
622 | - } |
|
623 | - |
|
624 | - $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
625 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
626 | - |
|
627 | - $qb->orderBy('id'); |
|
628 | - |
|
629 | - $cursor = $qb->execute(); |
|
630 | - $shares = []; |
|
631 | - while ($data = $cursor->fetch()) { |
|
632 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
633 | - } |
|
634 | - $cursor->closeCursor(); |
|
635 | - |
|
636 | - return $shares; |
|
637 | - } |
|
638 | - |
|
639 | - /** |
|
640 | - * @inheritdoc |
|
641 | - */ |
|
642 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
643 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
644 | - $qb->select('*') |
|
645 | - ->from('share'); |
|
646 | - |
|
647 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
648 | - |
|
649 | - /** |
|
650 | - * Reshares for this user are shares where they are the owner. |
|
651 | - */ |
|
652 | - if ($reshares === false) { |
|
653 | - //Special case for old shares created via the web UI |
|
654 | - $or1 = $qb->expr()->andX( |
|
655 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
656 | - $qb->expr()->isNull('uid_initiator') |
|
657 | - ); |
|
658 | - |
|
659 | - $qb->andWhere( |
|
660 | - $qb->expr()->orX( |
|
661 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
662 | - $or1 |
|
663 | - ) |
|
664 | - ); |
|
665 | - } else { |
|
666 | - $qb->andWhere( |
|
667 | - $qb->expr()->orX( |
|
668 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
669 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
670 | - ) |
|
671 | - ); |
|
672 | - } |
|
673 | - |
|
674 | - if ($node !== null) { |
|
675 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
676 | - } |
|
677 | - |
|
678 | - if ($limit !== -1) { |
|
679 | - $qb->setMaxResults($limit); |
|
680 | - } |
|
681 | - |
|
682 | - $qb->setFirstResult($offset); |
|
683 | - $qb->orderBy('id'); |
|
684 | - |
|
685 | - $cursor = $qb->execute(); |
|
686 | - $shares = []; |
|
687 | - while($data = $cursor->fetch()) { |
|
688 | - $shares[] = $this->createShareObject($data); |
|
689 | - } |
|
690 | - $cursor->closeCursor(); |
|
691 | - |
|
692 | - return $shares; |
|
693 | - } |
|
694 | - |
|
695 | - /** |
|
696 | - * @inheritdoc |
|
697 | - */ |
|
698 | - public function getShareById($id, $recipientId = null) { |
|
699 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | - |
|
701 | - $qb->select('*') |
|
702 | - ->from('share') |
|
703 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
704 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
705 | - |
|
706 | - $cursor = $qb->execute(); |
|
707 | - $data = $cursor->fetch(); |
|
708 | - $cursor->closeCursor(); |
|
709 | - |
|
710 | - if ($data === false) { |
|
711 | - throw new ShareNotFound(); |
|
712 | - } |
|
713 | - |
|
714 | - try { |
|
715 | - $share = $this->createShareObject($data); |
|
716 | - } catch (InvalidShare $e) { |
|
717 | - throw new ShareNotFound(); |
|
718 | - } |
|
719 | - |
|
720 | - return $share; |
|
721 | - } |
|
722 | - |
|
723 | - /** |
|
724 | - * Get shares for a given path |
|
725 | - * |
|
726 | - * @param \OCP\Files\Node $path |
|
727 | - * @return IShare[] |
|
728 | - */ |
|
729 | - public function getSharesByPath(Node $path) { |
|
730 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
731 | - |
|
732 | - $cursor = $qb->select('*') |
|
733 | - ->from('share') |
|
734 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
735 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
736 | - ->execute(); |
|
737 | - |
|
738 | - $shares = []; |
|
739 | - while($data = $cursor->fetch()) { |
|
740 | - $shares[] = $this->createShareObject($data); |
|
741 | - } |
|
742 | - $cursor->closeCursor(); |
|
743 | - |
|
744 | - return $shares; |
|
745 | - } |
|
746 | - |
|
747 | - /** |
|
748 | - * @inheritdoc |
|
749 | - */ |
|
750 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
751 | - /** @var IShare[] $shares */ |
|
752 | - $shares = []; |
|
753 | - |
|
754 | - //Get shares directly with this user |
|
755 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | - $qb->select('*') |
|
757 | - ->from('share'); |
|
758 | - |
|
759 | - // Order by id |
|
760 | - $qb->orderBy('id'); |
|
761 | - |
|
762 | - // Set limit and offset |
|
763 | - if ($limit !== -1) { |
|
764 | - $qb->setMaxResults($limit); |
|
765 | - } |
|
766 | - $qb->setFirstResult($offset); |
|
767 | - |
|
768 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
769 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
770 | - |
|
771 | - // Filter by node if provided |
|
772 | - if ($node !== null) { |
|
773 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
774 | - } |
|
775 | - |
|
776 | - $cursor = $qb->execute(); |
|
777 | - |
|
778 | - while($data = $cursor->fetch()) { |
|
779 | - $shares[] = $this->createShareObject($data); |
|
780 | - } |
|
781 | - $cursor->closeCursor(); |
|
782 | - |
|
783 | - |
|
784 | - return $shares; |
|
785 | - } |
|
786 | - |
|
787 | - /** |
|
788 | - * Get a share by token |
|
789 | - * |
|
790 | - * @param string $token |
|
791 | - * @return IShare |
|
792 | - * @throws ShareNotFound |
|
793 | - */ |
|
794 | - public function getShareByToken($token) { |
|
795 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
796 | - |
|
797 | - $cursor = $qb->select('*') |
|
798 | - ->from('share') |
|
799 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
800 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
801 | - ->execute(); |
|
802 | - |
|
803 | - $data = $cursor->fetch(); |
|
804 | - |
|
805 | - if ($data === false) { |
|
806 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
807 | - } |
|
808 | - |
|
809 | - try { |
|
810 | - $share = $this->createShareObject($data); |
|
811 | - } catch (InvalidShare $e) { |
|
812 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
813 | - } |
|
814 | - |
|
815 | - return $share; |
|
816 | - } |
|
817 | - |
|
818 | - /** |
|
819 | - * get database row of a give share |
|
820 | - * |
|
821 | - * @param $id |
|
822 | - * @return array |
|
823 | - * @throws ShareNotFound |
|
824 | - */ |
|
825 | - private function getRawShare($id) { |
|
826 | - |
|
827 | - // Now fetch the inserted share and create a complete share object |
|
828 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
829 | - $qb->select('*') |
|
830 | - ->from('share') |
|
831 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
832 | - |
|
833 | - $cursor = $qb->execute(); |
|
834 | - $data = $cursor->fetch(); |
|
835 | - $cursor->closeCursor(); |
|
836 | - |
|
837 | - if ($data === false) { |
|
838 | - throw new ShareNotFound; |
|
839 | - } |
|
840 | - |
|
841 | - return $data; |
|
842 | - } |
|
843 | - |
|
844 | - /** |
|
845 | - * Create a share object from an database row |
|
846 | - * |
|
847 | - * @param array $data |
|
848 | - * @return IShare |
|
849 | - * @throws InvalidShare |
|
850 | - * @throws ShareNotFound |
|
851 | - */ |
|
852 | - private function createShareObject($data) { |
|
853 | - |
|
854 | - $share = new Share($this->rootFolder, $this->userManager); |
|
855 | - $share->setId((int)$data['id']) |
|
856 | - ->setShareType((int)$data['share_type']) |
|
857 | - ->setPermissions((int)$data['permissions']) |
|
858 | - ->setTarget($data['file_target']) |
|
859 | - ->setMailSend((bool)$data['mail_send']) |
|
860 | - ->setToken($data['token']); |
|
861 | - |
|
862 | - $shareTime = new \DateTime(); |
|
863 | - $shareTime->setTimestamp((int)$data['stime']); |
|
864 | - $share->setShareTime($shareTime); |
|
865 | - $share->setSharedWith($data['share_with']); |
|
866 | - |
|
867 | - if ($data['uid_initiator'] !== null) { |
|
868 | - $share->setShareOwner($data['uid_owner']); |
|
869 | - $share->setSharedBy($data['uid_initiator']); |
|
870 | - } else { |
|
871 | - //OLD SHARE |
|
872 | - $share->setSharedBy($data['uid_owner']); |
|
873 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
874 | - |
|
875 | - $owner = $path->getOwner(); |
|
876 | - $share->setShareOwner($owner->getUID()); |
|
877 | - } |
|
878 | - |
|
879 | - $share->setNodeId((int)$data['file_source']); |
|
880 | - $share->setNodeType($data['item_type']); |
|
881 | - |
|
882 | - $share->setProviderId($this->identifier()); |
|
883 | - |
|
884 | - return $share; |
|
885 | - } |
|
886 | - |
|
887 | - /** |
|
888 | - * Get the node with file $id for $user |
|
889 | - * |
|
890 | - * @param string $userId |
|
891 | - * @param int $id |
|
892 | - * @return \OCP\Files\File|\OCP\Files\Folder |
|
893 | - * @throws InvalidShare |
|
894 | - */ |
|
895 | - private function getNode($userId, $id) { |
|
896 | - try { |
|
897 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
898 | - } catch (NotFoundException $e) { |
|
899 | - throw new InvalidShare(); |
|
900 | - } |
|
901 | - |
|
902 | - $nodes = $userFolder->getById($id); |
|
903 | - |
|
904 | - if (empty($nodes)) { |
|
905 | - throw new InvalidShare(); |
|
906 | - } |
|
907 | - |
|
908 | - return $nodes[0]; |
|
909 | - } |
|
910 | - |
|
911 | - /** |
|
912 | - * A user is deleted from the system |
|
913 | - * So clean up the relevant shares. |
|
914 | - * |
|
915 | - * @param string $uid |
|
916 | - * @param int $shareType |
|
917 | - */ |
|
918 | - public function userDeleted($uid, $shareType) { |
|
919 | - //TODO: probabaly a good idea to send unshare info to remote servers |
|
920 | - |
|
921 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
922 | - |
|
923 | - $qb->delete('share') |
|
924 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
925 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
926 | - ->execute(); |
|
927 | - } |
|
928 | - |
|
929 | - /** |
|
930 | - * This provider does not handle groups |
|
931 | - * |
|
932 | - * @param string $gid |
|
933 | - */ |
|
934 | - public function groupDeleted($gid) { |
|
935 | - // We don't handle groups here |
|
936 | - } |
|
937 | - |
|
938 | - /** |
|
939 | - * This provider does not handle groups |
|
940 | - * |
|
941 | - * @param string $uid |
|
942 | - * @param string $gid |
|
943 | - */ |
|
944 | - public function userDeletedFromGroup($uid, $gid) { |
|
945 | - // We don't handle groups here |
|
946 | - } |
|
947 | - |
|
948 | - /** |
|
949 | - * check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
950 | - * |
|
951 | - * @return bool |
|
952 | - */ |
|
953 | - public function isOutgoingServer2serverShareEnabled() { |
|
954 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
955 | - return false; |
|
956 | - } |
|
957 | - $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
958 | - return ($result === 'yes'); |
|
959 | - } |
|
960 | - |
|
961 | - /** |
|
962 | - * check if users are allowed to mount public links from other Nextclouds |
|
963 | - * |
|
964 | - * @return bool |
|
965 | - */ |
|
966 | - public function isIncomingServer2serverShareEnabled() { |
|
967 | - if ($this->gsConfig->onlyInternalFederation()) { |
|
968 | - return false; |
|
969 | - } |
|
970 | - $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
971 | - return ($result === 'yes'); |
|
972 | - } |
|
973 | - |
|
974 | - /** |
|
975 | - * Check if querying sharees on the lookup server is enabled |
|
976 | - * |
|
977 | - * @return bool |
|
978 | - */ |
|
979 | - public function isLookupServerQueriesEnabled() { |
|
980 | - // in a global scale setup we should always query the lookup server |
|
981 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
982 | - return true; |
|
983 | - } |
|
984 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
985 | - return ($result === 'yes'); |
|
986 | - } |
|
987 | - |
|
988 | - |
|
989 | - /** |
|
990 | - * Check if it is allowed to publish user specific data to the lookup server |
|
991 | - * |
|
992 | - * @return bool |
|
993 | - */ |
|
994 | - public function isLookupServerUploadEnabled() { |
|
995 | - // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
996 | - if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
997 | - return false; |
|
998 | - } |
|
999 | - $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); |
|
1000 | - return ($result === 'yes'); |
|
1001 | - } |
|
1002 | - |
|
1003 | - /** |
|
1004 | - * @inheritdoc |
|
1005 | - */ |
|
1006 | - public function getAccessList($nodes, $currentAccess) { |
|
1007 | - $ids = []; |
|
1008 | - foreach ($nodes as $node) { |
|
1009 | - $ids[] = $node->getId(); |
|
1010 | - } |
|
1011 | - |
|
1012 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1013 | - $qb->select('share_with', 'token', 'file_source') |
|
1014 | - ->from('share') |
|
1015 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
1016 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1017 | - ->andWhere($qb->expr()->orX( |
|
1018 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1019 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1020 | - )); |
|
1021 | - $cursor = $qb->execute(); |
|
1022 | - |
|
1023 | - if ($currentAccess === false) { |
|
1024 | - $remote = $cursor->fetch() !== false; |
|
1025 | - $cursor->closeCursor(); |
|
1026 | - |
|
1027 | - return ['remote' => $remote]; |
|
1028 | - } |
|
1029 | - |
|
1030 | - $remote = []; |
|
1031 | - while ($row = $cursor->fetch()) { |
|
1032 | - $remote[$row['share_with']] = [ |
|
1033 | - 'node_id' => $row['file_source'], |
|
1034 | - 'token' => $row['token'], |
|
1035 | - ]; |
|
1036 | - } |
|
1037 | - $cursor->closeCursor(); |
|
1038 | - |
|
1039 | - return ['remote' => $remote]; |
|
1040 | - } |
|
476 | + return $share; |
|
477 | + } |
|
478 | + |
|
479 | + /** |
|
480 | + * Get all children of this share |
|
481 | + * |
|
482 | + * @param IShare $parent |
|
483 | + * @return IShare[] |
|
484 | + */ |
|
485 | + public function getChildren(IShare $parent) { |
|
486 | + $children = []; |
|
487 | + |
|
488 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
489 | + $qb->select('*') |
|
490 | + ->from('share') |
|
491 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
492 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
493 | + ->orderBy('id'); |
|
494 | + |
|
495 | + $cursor = $qb->execute(); |
|
496 | + while($data = $cursor->fetch()) { |
|
497 | + $children[] = $this->createShareObject($data); |
|
498 | + } |
|
499 | + $cursor->closeCursor(); |
|
500 | + |
|
501 | + return $children; |
|
502 | + } |
|
503 | + |
|
504 | + /** |
|
505 | + * Delete a share (owner unShares the file) |
|
506 | + * |
|
507 | + * @param IShare $share |
|
508 | + */ |
|
509 | + public function delete(IShare $share) { |
|
510 | + |
|
511 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith()); |
|
512 | + |
|
513 | + $isOwner = false; |
|
514 | + |
|
515 | + $this->removeShareFromTable($share); |
|
516 | + |
|
517 | + // if the local user is the owner we can send the unShare request directly... |
|
518 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
519 | + $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken()); |
|
520 | + $this->revokeShare($share, true); |
|
521 | + $isOwner = true; |
|
522 | + } else { // ... if not we need to correct ID for the unShare request |
|
523 | + $remoteId = $this->getRemoteId($share); |
|
524 | + $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken()); |
|
525 | + $this->revokeShare($share, false); |
|
526 | + } |
|
527 | + |
|
528 | + // send revoke notification to the other user, if initiator and owner are not the same user |
|
529 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
530 | + $remoteId = $this->getRemoteId($share); |
|
531 | + if ($isOwner) { |
|
532 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
533 | + } else { |
|
534 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
535 | + } |
|
536 | + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
537 | + } |
|
538 | + } |
|
539 | + |
|
540 | + /** |
|
541 | + * in case of a re-share we need to send the other use (initiator or owner) |
|
542 | + * a message that the file was unshared |
|
543 | + * |
|
544 | + * @param IShare $share |
|
545 | + * @param bool $isOwner the user can either be the owner or the user who re-sahred it |
|
546 | + * @throws ShareNotFound |
|
547 | + * @throws \OC\HintException |
|
548 | + */ |
|
549 | + protected function revokeShare($share, $isOwner) { |
|
550 | + // also send a unShare request to the initiator, if this is a different user than the owner |
|
551 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
552 | + if ($isOwner) { |
|
553 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
554 | + } else { |
|
555 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner()); |
|
556 | + } |
|
557 | + $remoteId = $this->getRemoteId($share); |
|
558 | + $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken()); |
|
559 | + } |
|
560 | + } |
|
561 | + |
|
562 | + /** |
|
563 | + * remove share from table |
|
564 | + * |
|
565 | + * @param IShare $share |
|
566 | + */ |
|
567 | + public function removeShareFromTable(IShare $share) { |
|
568 | + $this->removeShareFromTableById($share->getId()); |
|
569 | + } |
|
570 | + |
|
571 | + /** |
|
572 | + * remove share from table |
|
573 | + * |
|
574 | + * @param string $shareId |
|
575 | + */ |
|
576 | + private function removeShareFromTableById($shareId) { |
|
577 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
578 | + $qb->delete('share') |
|
579 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
580 | + $qb->execute(); |
|
581 | + |
|
582 | + $qb->delete('federated_reshares') |
|
583 | + ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); |
|
584 | + $qb->execute(); |
|
585 | + } |
|
586 | + |
|
587 | + /** |
|
588 | + * @inheritdoc |
|
589 | + */ |
|
590 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
591 | + // nothing to do here. Technically deleteFromSelf in the context of federated |
|
592 | + // shares is a umount of a external storage. This is handled here |
|
593 | + // apps/files_sharing/lib/external/manager.php |
|
594 | + // TODO move this code over to this app |
|
595 | + } |
|
596 | + |
|
597 | + |
|
598 | + public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
599 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
600 | + $qb->select('*') |
|
601 | + ->from('share', 's') |
|
602 | + ->andWhere($qb->expr()->orX( |
|
603 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
604 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
605 | + )) |
|
606 | + ->andWhere( |
|
607 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)) |
|
608 | + ); |
|
609 | + |
|
610 | + /** |
|
611 | + * Reshares for this user are shares where they are the owner. |
|
612 | + */ |
|
613 | + if ($reshares === false) { |
|
614 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
615 | + } else { |
|
616 | + $qb->andWhere( |
|
617 | + $qb->expr()->orX( |
|
618 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
619 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
620 | + ) |
|
621 | + ); |
|
622 | + } |
|
623 | + |
|
624 | + $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
625 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
626 | + |
|
627 | + $qb->orderBy('id'); |
|
628 | + |
|
629 | + $cursor = $qb->execute(); |
|
630 | + $shares = []; |
|
631 | + while ($data = $cursor->fetch()) { |
|
632 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
633 | + } |
|
634 | + $cursor->closeCursor(); |
|
635 | + |
|
636 | + return $shares; |
|
637 | + } |
|
638 | + |
|
639 | + /** |
|
640 | + * @inheritdoc |
|
641 | + */ |
|
642 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
643 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
644 | + $qb->select('*') |
|
645 | + ->from('share'); |
|
646 | + |
|
647 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
648 | + |
|
649 | + /** |
|
650 | + * Reshares for this user are shares where they are the owner. |
|
651 | + */ |
|
652 | + if ($reshares === false) { |
|
653 | + //Special case for old shares created via the web UI |
|
654 | + $or1 = $qb->expr()->andX( |
|
655 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
656 | + $qb->expr()->isNull('uid_initiator') |
|
657 | + ); |
|
658 | + |
|
659 | + $qb->andWhere( |
|
660 | + $qb->expr()->orX( |
|
661 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
662 | + $or1 |
|
663 | + ) |
|
664 | + ); |
|
665 | + } else { |
|
666 | + $qb->andWhere( |
|
667 | + $qb->expr()->orX( |
|
668 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
669 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
670 | + ) |
|
671 | + ); |
|
672 | + } |
|
673 | + |
|
674 | + if ($node !== null) { |
|
675 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
676 | + } |
|
677 | + |
|
678 | + if ($limit !== -1) { |
|
679 | + $qb->setMaxResults($limit); |
|
680 | + } |
|
681 | + |
|
682 | + $qb->setFirstResult($offset); |
|
683 | + $qb->orderBy('id'); |
|
684 | + |
|
685 | + $cursor = $qb->execute(); |
|
686 | + $shares = []; |
|
687 | + while($data = $cursor->fetch()) { |
|
688 | + $shares[] = $this->createShareObject($data); |
|
689 | + } |
|
690 | + $cursor->closeCursor(); |
|
691 | + |
|
692 | + return $shares; |
|
693 | + } |
|
694 | + |
|
695 | + /** |
|
696 | + * @inheritdoc |
|
697 | + */ |
|
698 | + public function getShareById($id, $recipientId = null) { |
|
699 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | + |
|
701 | + $qb->select('*') |
|
702 | + ->from('share') |
|
703 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
704 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
705 | + |
|
706 | + $cursor = $qb->execute(); |
|
707 | + $data = $cursor->fetch(); |
|
708 | + $cursor->closeCursor(); |
|
709 | + |
|
710 | + if ($data === false) { |
|
711 | + throw new ShareNotFound(); |
|
712 | + } |
|
713 | + |
|
714 | + try { |
|
715 | + $share = $this->createShareObject($data); |
|
716 | + } catch (InvalidShare $e) { |
|
717 | + throw new ShareNotFound(); |
|
718 | + } |
|
719 | + |
|
720 | + return $share; |
|
721 | + } |
|
722 | + |
|
723 | + /** |
|
724 | + * Get shares for a given path |
|
725 | + * |
|
726 | + * @param \OCP\Files\Node $path |
|
727 | + * @return IShare[] |
|
728 | + */ |
|
729 | + public function getSharesByPath(Node $path) { |
|
730 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
731 | + |
|
732 | + $cursor = $qb->select('*') |
|
733 | + ->from('share') |
|
734 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
735 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
736 | + ->execute(); |
|
737 | + |
|
738 | + $shares = []; |
|
739 | + while($data = $cursor->fetch()) { |
|
740 | + $shares[] = $this->createShareObject($data); |
|
741 | + } |
|
742 | + $cursor->closeCursor(); |
|
743 | + |
|
744 | + return $shares; |
|
745 | + } |
|
746 | + |
|
747 | + /** |
|
748 | + * @inheritdoc |
|
749 | + */ |
|
750 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
751 | + /** @var IShare[] $shares */ |
|
752 | + $shares = []; |
|
753 | + |
|
754 | + //Get shares directly with this user |
|
755 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | + $qb->select('*') |
|
757 | + ->from('share'); |
|
758 | + |
|
759 | + // Order by id |
|
760 | + $qb->orderBy('id'); |
|
761 | + |
|
762 | + // Set limit and offset |
|
763 | + if ($limit !== -1) { |
|
764 | + $qb->setMaxResults($limit); |
|
765 | + } |
|
766 | + $qb->setFirstResult($offset); |
|
767 | + |
|
768 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))); |
|
769 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
770 | + |
|
771 | + // Filter by node if provided |
|
772 | + if ($node !== null) { |
|
773 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
774 | + } |
|
775 | + |
|
776 | + $cursor = $qb->execute(); |
|
777 | + |
|
778 | + while($data = $cursor->fetch()) { |
|
779 | + $shares[] = $this->createShareObject($data); |
|
780 | + } |
|
781 | + $cursor->closeCursor(); |
|
782 | + |
|
783 | + |
|
784 | + return $shares; |
|
785 | + } |
|
786 | + |
|
787 | + /** |
|
788 | + * Get a share by token |
|
789 | + * |
|
790 | + * @param string $token |
|
791 | + * @return IShare |
|
792 | + * @throws ShareNotFound |
|
793 | + */ |
|
794 | + public function getShareByToken($token) { |
|
795 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
796 | + |
|
797 | + $cursor = $qb->select('*') |
|
798 | + ->from('share') |
|
799 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))) |
|
800 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
801 | + ->execute(); |
|
802 | + |
|
803 | + $data = $cursor->fetch(); |
|
804 | + |
|
805 | + if ($data === false) { |
|
806 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
807 | + } |
|
808 | + |
|
809 | + try { |
|
810 | + $share = $this->createShareObject($data); |
|
811 | + } catch (InvalidShare $e) { |
|
812 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
813 | + } |
|
814 | + |
|
815 | + return $share; |
|
816 | + } |
|
817 | + |
|
818 | + /** |
|
819 | + * get database row of a give share |
|
820 | + * |
|
821 | + * @param $id |
|
822 | + * @return array |
|
823 | + * @throws ShareNotFound |
|
824 | + */ |
|
825 | + private function getRawShare($id) { |
|
826 | + |
|
827 | + // Now fetch the inserted share and create a complete share object |
|
828 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
829 | + $qb->select('*') |
|
830 | + ->from('share') |
|
831 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
832 | + |
|
833 | + $cursor = $qb->execute(); |
|
834 | + $data = $cursor->fetch(); |
|
835 | + $cursor->closeCursor(); |
|
836 | + |
|
837 | + if ($data === false) { |
|
838 | + throw new ShareNotFound; |
|
839 | + } |
|
840 | + |
|
841 | + return $data; |
|
842 | + } |
|
843 | + |
|
844 | + /** |
|
845 | + * Create a share object from an database row |
|
846 | + * |
|
847 | + * @param array $data |
|
848 | + * @return IShare |
|
849 | + * @throws InvalidShare |
|
850 | + * @throws ShareNotFound |
|
851 | + */ |
|
852 | + private function createShareObject($data) { |
|
853 | + |
|
854 | + $share = new Share($this->rootFolder, $this->userManager); |
|
855 | + $share->setId((int)$data['id']) |
|
856 | + ->setShareType((int)$data['share_type']) |
|
857 | + ->setPermissions((int)$data['permissions']) |
|
858 | + ->setTarget($data['file_target']) |
|
859 | + ->setMailSend((bool)$data['mail_send']) |
|
860 | + ->setToken($data['token']); |
|
861 | + |
|
862 | + $shareTime = new \DateTime(); |
|
863 | + $shareTime->setTimestamp((int)$data['stime']); |
|
864 | + $share->setShareTime($shareTime); |
|
865 | + $share->setSharedWith($data['share_with']); |
|
866 | + |
|
867 | + if ($data['uid_initiator'] !== null) { |
|
868 | + $share->setShareOwner($data['uid_owner']); |
|
869 | + $share->setSharedBy($data['uid_initiator']); |
|
870 | + } else { |
|
871 | + //OLD SHARE |
|
872 | + $share->setSharedBy($data['uid_owner']); |
|
873 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
874 | + |
|
875 | + $owner = $path->getOwner(); |
|
876 | + $share->setShareOwner($owner->getUID()); |
|
877 | + } |
|
878 | + |
|
879 | + $share->setNodeId((int)$data['file_source']); |
|
880 | + $share->setNodeType($data['item_type']); |
|
881 | + |
|
882 | + $share->setProviderId($this->identifier()); |
|
883 | + |
|
884 | + return $share; |
|
885 | + } |
|
886 | + |
|
887 | + /** |
|
888 | + * Get the node with file $id for $user |
|
889 | + * |
|
890 | + * @param string $userId |
|
891 | + * @param int $id |
|
892 | + * @return \OCP\Files\File|\OCP\Files\Folder |
|
893 | + * @throws InvalidShare |
|
894 | + */ |
|
895 | + private function getNode($userId, $id) { |
|
896 | + try { |
|
897 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
898 | + } catch (NotFoundException $e) { |
|
899 | + throw new InvalidShare(); |
|
900 | + } |
|
901 | + |
|
902 | + $nodes = $userFolder->getById($id); |
|
903 | + |
|
904 | + if (empty($nodes)) { |
|
905 | + throw new InvalidShare(); |
|
906 | + } |
|
907 | + |
|
908 | + return $nodes[0]; |
|
909 | + } |
|
910 | + |
|
911 | + /** |
|
912 | + * A user is deleted from the system |
|
913 | + * So clean up the relevant shares. |
|
914 | + * |
|
915 | + * @param string $uid |
|
916 | + * @param int $shareType |
|
917 | + */ |
|
918 | + public function userDeleted($uid, $shareType) { |
|
919 | + //TODO: probabaly a good idea to send unshare info to remote servers |
|
920 | + |
|
921 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
922 | + |
|
923 | + $qb->delete('share') |
|
924 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
925 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
926 | + ->execute(); |
|
927 | + } |
|
928 | + |
|
929 | + /** |
|
930 | + * This provider does not handle groups |
|
931 | + * |
|
932 | + * @param string $gid |
|
933 | + */ |
|
934 | + public function groupDeleted($gid) { |
|
935 | + // We don't handle groups here |
|
936 | + } |
|
937 | + |
|
938 | + /** |
|
939 | + * This provider does not handle groups |
|
940 | + * |
|
941 | + * @param string $uid |
|
942 | + * @param string $gid |
|
943 | + */ |
|
944 | + public function userDeletedFromGroup($uid, $gid) { |
|
945 | + // We don't handle groups here |
|
946 | + } |
|
947 | + |
|
948 | + /** |
|
949 | + * check if users from other Nextcloud instances are allowed to mount public links share by this instance |
|
950 | + * |
|
951 | + * @return bool |
|
952 | + */ |
|
953 | + public function isOutgoingServer2serverShareEnabled() { |
|
954 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
955 | + return false; |
|
956 | + } |
|
957 | + $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); |
|
958 | + return ($result === 'yes'); |
|
959 | + } |
|
960 | + |
|
961 | + /** |
|
962 | + * check if users are allowed to mount public links from other Nextclouds |
|
963 | + * |
|
964 | + * @return bool |
|
965 | + */ |
|
966 | + public function isIncomingServer2serverShareEnabled() { |
|
967 | + if ($this->gsConfig->onlyInternalFederation()) { |
|
968 | + return false; |
|
969 | + } |
|
970 | + $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); |
|
971 | + return ($result === 'yes'); |
|
972 | + } |
|
973 | + |
|
974 | + /** |
|
975 | + * Check if querying sharees on the lookup server is enabled |
|
976 | + * |
|
977 | + * @return bool |
|
978 | + */ |
|
979 | + public function isLookupServerQueriesEnabled() { |
|
980 | + // in a global scale setup we should always query the lookup server |
|
981 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
982 | + return true; |
|
983 | + } |
|
984 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
985 | + return ($result === 'yes'); |
|
986 | + } |
|
987 | + |
|
988 | + |
|
989 | + /** |
|
990 | + * Check if it is allowed to publish user specific data to the lookup server |
|
991 | + * |
|
992 | + * @return bool |
|
993 | + */ |
|
994 | + public function isLookupServerUploadEnabled() { |
|
995 | + // in a global scale setup the admin is responsible to keep the lookup server up-to-date |
|
996 | + if ($this->gsConfig->isGlobalScaleEnabled()) { |
|
997 | + return false; |
|
998 | + } |
|
999 | + $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); |
|
1000 | + return ($result === 'yes'); |
|
1001 | + } |
|
1002 | + |
|
1003 | + /** |
|
1004 | + * @inheritdoc |
|
1005 | + */ |
|
1006 | + public function getAccessList($nodes, $currentAccess) { |
|
1007 | + $ids = []; |
|
1008 | + foreach ($nodes as $node) { |
|
1009 | + $ids[] = $node->getId(); |
|
1010 | + } |
|
1011 | + |
|
1012 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1013 | + $qb->select('share_with', 'token', 'file_source') |
|
1014 | + ->from('share') |
|
1015 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))) |
|
1016 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1017 | + ->andWhere($qb->expr()->orX( |
|
1018 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1019 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1020 | + )); |
|
1021 | + $cursor = $qb->execute(); |
|
1022 | + |
|
1023 | + if ($currentAccess === false) { |
|
1024 | + $remote = $cursor->fetch() !== false; |
|
1025 | + $cursor->closeCursor(); |
|
1026 | + |
|
1027 | + return ['remote' => $remote]; |
|
1028 | + } |
|
1029 | + |
|
1030 | + $remote = []; |
|
1031 | + while ($row = $cursor->fetch()) { |
|
1032 | + $remote[$row['share_with']] = [ |
|
1033 | + 'node_id' => $row['file_source'], |
|
1034 | + 'token' => $row['token'], |
|
1035 | + ]; |
|
1036 | + } |
|
1037 | + $cursor->closeCursor(); |
|
1038 | + |
|
1039 | + return ['remote' => $remote]; |
|
1040 | + } |
|
1041 | 1041 | } |
@@ -51,639 +51,639 @@ |
||
51 | 51 | |
52 | 52 | class RequestHandlerController extends OCSController { |
53 | 53 | |
54 | - /** @var FederatedShareProvider */ |
|
55 | - private $federatedShareProvider; |
|
56 | - |
|
57 | - /** @var IDBConnection */ |
|
58 | - private $connection; |
|
59 | - |
|
60 | - /** @var Share\IManager */ |
|
61 | - private $shareManager; |
|
62 | - |
|
63 | - /** @var Notifications */ |
|
64 | - private $notifications; |
|
65 | - |
|
66 | - /** @var AddressHandler */ |
|
67 | - private $addressHandler; |
|
68 | - |
|
69 | - /** @var IUserManager */ |
|
70 | - private $userManager; |
|
71 | - |
|
72 | - /** @var string */ |
|
73 | - private $shareTable = 'share'; |
|
74 | - |
|
75 | - /** @var ICloudIdManager */ |
|
76 | - private $cloudIdManager; |
|
77 | - |
|
78 | - /** @var ILogger */ |
|
79 | - private $logger; |
|
80 | - |
|
81 | - /** |
|
82 | - * Server2Server constructor. |
|
83 | - * |
|
84 | - * @param string $appName |
|
85 | - * @param IRequest $request |
|
86 | - * @param FederatedShareProvider $federatedShareProvider |
|
87 | - * @param IDBConnection $connection |
|
88 | - * @param Share\IManager $shareManager |
|
89 | - * @param Notifications $notifications |
|
90 | - * @param AddressHandler $addressHandler |
|
91 | - * @param IUserManager $userManager |
|
92 | - * @param ICloudIdManager $cloudIdManager |
|
93 | - */ |
|
94 | - public function __construct($appName, |
|
95 | - IRequest $request, |
|
96 | - FederatedShareProvider $federatedShareProvider, |
|
97 | - IDBConnection $connection, |
|
98 | - Share\IManager $shareManager, |
|
99 | - Notifications $notifications, |
|
100 | - AddressHandler $addressHandler, |
|
101 | - IUserManager $userManager, |
|
102 | - ICloudIdManager $cloudIdManager, |
|
103 | - ILogger $logger |
|
104 | - ) { |
|
105 | - parent::__construct($appName, $request); |
|
106 | - |
|
107 | - $this->federatedShareProvider = $federatedShareProvider; |
|
108 | - $this->connection = $connection; |
|
109 | - $this->shareManager = $shareManager; |
|
110 | - $this->notifications = $notifications; |
|
111 | - $this->addressHandler = $addressHandler; |
|
112 | - $this->userManager = $userManager; |
|
113 | - $this->cloudIdManager = $cloudIdManager; |
|
114 | - $this->logger = $logger; |
|
115 | - } |
|
116 | - |
|
117 | - /** |
|
118 | - * @NoCSRFRequired |
|
119 | - * @PublicPage |
|
120 | - * |
|
121 | - * create a new share |
|
122 | - * |
|
123 | - * @return Http\DataResponse |
|
124 | - * @throws OCSException |
|
125 | - */ |
|
126 | - public function createShare() { |
|
127 | - |
|
128 | - if (!$this->isS2SEnabled(true)) { |
|
129 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
130 | - } |
|
131 | - |
|
132 | - $remote = isset($_POST['remote']) ? $_POST['remote'] : null; |
|
133 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
134 | - $name = isset($_POST['name']) ? $_POST['name'] : null; |
|
135 | - $owner = isset($_POST['owner']) ? $_POST['owner'] : null; |
|
136 | - $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; |
|
137 | - $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; |
|
138 | - $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; |
|
139 | - $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; |
|
140 | - $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; |
|
141 | - |
|
142 | - if ($remote && $token && $name && $owner && $remoteId && $shareWith) { |
|
143 | - |
|
144 | - if (!\OCP\Util::isValidFileName($name)) { |
|
145 | - throw new OCSException('The mountpoint name contains invalid characters.', 400); |
|
146 | - } |
|
147 | - |
|
148 | - // FIXME this should be a method in the user management instead |
|
149 | - $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); |
|
150 | - \OCP\Util::emitHook( |
|
151 | - '\OCA\Files_Sharing\API\Server2Server', |
|
152 | - 'preLoginNameUsedAsUserName', |
|
153 | - array('uid' => &$shareWith) |
|
154 | - ); |
|
155 | - $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); |
|
156 | - |
|
157 | - if (!\OC::$server->getUserManager()->userExists($shareWith)) { |
|
158 | - throw new OCSException('User does not exists', 400); |
|
159 | - } |
|
160 | - |
|
161 | - \OC_Util::setupFS($shareWith); |
|
162 | - |
|
163 | - $externalManager = new \OCA\Files_Sharing\External\Manager( |
|
164 | - \OC::$server->getDatabaseConnection(), |
|
165 | - \OC\Files\Filesystem::getMountManager(), |
|
166 | - \OC\Files\Filesystem::getLoader(), |
|
167 | - \OC::$server->getHTTPClientService(), |
|
168 | - \OC::$server->getNotificationManager(), |
|
169 | - \OC::$server->query(\OCP\OCS\IDiscoveryService::class), |
|
170 | - $shareWith |
|
171 | - ); |
|
172 | - |
|
173 | - try { |
|
174 | - $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); |
|
175 | - $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); |
|
176 | - |
|
177 | - if ($ownerFederatedId === null) { |
|
178 | - $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); |
|
179 | - } |
|
180 | - // if the owner of the share and the initiator are the same user |
|
181 | - // we also complete the federated share ID for the initiator |
|
182 | - if ($sharedByFederatedId === null && $owner === $sharedBy) { |
|
183 | - $sharedByFederatedId = $ownerFederatedId; |
|
184 | - } |
|
185 | - |
|
186 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
187 | - $event->setApp('files_sharing') |
|
188 | - ->setType('remote_share') |
|
189 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) |
|
190 | - ->setAffectedUser($shareWith) |
|
191 | - ->setObject('remote_share', (int)$shareId, $name); |
|
192 | - \OC::$server->getActivityManager()->publish($event); |
|
193 | - |
|
194 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
195 | - |
|
196 | - $notificationManager = \OC::$server->getNotificationManager(); |
|
197 | - $notification = $notificationManager->createNotification(); |
|
198 | - $notification->setApp('files_sharing') |
|
199 | - ->setUser($shareWith) |
|
200 | - ->setDateTime(new \DateTime()) |
|
201 | - ->setObject('remote_share', $shareId) |
|
202 | - ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); |
|
203 | - |
|
204 | - $declineAction = $notification->createAction(); |
|
205 | - $declineAction->setLabel('decline') |
|
206 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); |
|
207 | - $notification->addAction($declineAction); |
|
208 | - |
|
209 | - $acceptAction = $notification->createAction(); |
|
210 | - $acceptAction->setLabel('accept') |
|
211 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); |
|
212 | - $notification->addAction($acceptAction); |
|
213 | - |
|
214 | - $notificationManager->notify($notification); |
|
215 | - |
|
216 | - return new Http\DataResponse(); |
|
217 | - } catch (\Exception $e) { |
|
218 | - $this->logger->logException($e, [ |
|
219 | - 'message' => 'Server can not add remote share.', |
|
220 | - 'level' => ILogger::ERROR, |
|
221 | - 'app' => 'files_sharing' |
|
222 | - ]); |
|
223 | - throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); |
|
224 | - } |
|
225 | - } |
|
226 | - |
|
227 | - throw new OCSException('server can not add remote share, missing parameter', 400); |
|
228 | - } |
|
229 | - |
|
230 | - /** |
|
231 | - * @NoCSRFRequired |
|
232 | - * @PublicPage |
|
233 | - * |
|
234 | - * create re-share on behalf of another user |
|
235 | - * |
|
236 | - * @param int $id |
|
237 | - * @return Http\DataResponse |
|
238 | - * @throws OCSBadRequestException |
|
239 | - * @throws OCSForbiddenException |
|
240 | - * @throws OCSNotFoundException |
|
241 | - */ |
|
242 | - public function reShare($id) { |
|
243 | - |
|
244 | - $token = $this->request->getParam('token', null); |
|
245 | - $shareWith = $this->request->getParam('shareWith', null); |
|
246 | - $permission = (int)$this->request->getParam('permission', null); |
|
247 | - $remoteId = (int)$this->request->getParam('remoteId', null); |
|
248 | - |
|
249 | - if ($id === null || |
|
250 | - $token === null || |
|
251 | - $shareWith === null || |
|
252 | - $permission === null || |
|
253 | - $remoteId === null |
|
254 | - ) { |
|
255 | - throw new OCSBadRequestException(); |
|
256 | - } |
|
257 | - |
|
258 | - try { |
|
259 | - $share = $this->federatedShareProvider->getShareById($id); |
|
260 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
261 | - throw new OCSNotFoundException(); |
|
262 | - } |
|
263 | - |
|
264 | - // don't allow to share a file back to the owner |
|
265 | - list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); |
|
266 | - $owner = $share->getShareOwner(); |
|
267 | - $currentServer = $this->addressHandler->generateRemoteURL(); |
|
268 | - if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) { |
|
269 | - throw new OCSForbiddenException(); |
|
270 | - } |
|
271 | - |
|
272 | - if ($this->verifyShare($share, $token)) { |
|
273 | - |
|
274 | - // check if re-sharing is allowed |
|
275 | - if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) { |
|
276 | - $share->setPermissions($share->getPermissions() & $permission); |
|
277 | - // the recipient of the initial share is now the initiator for the re-share |
|
278 | - $share->setSharedBy($share->getSharedWith()); |
|
279 | - $share->setSharedWith($shareWith); |
|
280 | - try { |
|
281 | - $result = $this->federatedShareProvider->create($share); |
|
282 | - $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); |
|
283 | - return new Http\DataResponse([ |
|
284 | - 'token' => $result->getToken(), |
|
285 | - 'remoteId' => $result->getId() |
|
286 | - ]); |
|
287 | - } catch (\Exception $e) { |
|
288 | - throw new OCSBadRequestException(); |
|
289 | - } |
|
290 | - } else { |
|
291 | - throw new OCSForbiddenException(); |
|
292 | - } |
|
293 | - } |
|
294 | - throw new OCSBadRequestException(); |
|
295 | - } |
|
296 | - |
|
297 | - /** |
|
298 | - * @NoCSRFRequired |
|
299 | - * @PublicPage |
|
300 | - * |
|
301 | - * accept server-to-server share |
|
302 | - * |
|
303 | - * @param int $id |
|
304 | - * @return Http\DataResponse |
|
305 | - * @throws OCSException |
|
306 | - */ |
|
307 | - public function acceptShare($id) { |
|
308 | - |
|
309 | - if (!$this->isS2SEnabled()) { |
|
310 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
311 | - } |
|
312 | - |
|
313 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
314 | - |
|
315 | - try { |
|
316 | - $share = $this->federatedShareProvider->getShareById($id); |
|
317 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
318 | - return new Http\DataResponse(); |
|
319 | - } |
|
320 | - |
|
321 | - if ($this->verifyShare($share, $token)) { |
|
322 | - $this->executeAcceptShare($share); |
|
323 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
324 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
325 | - $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
326 | - $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken()); |
|
327 | - } |
|
328 | - } |
|
329 | - |
|
330 | - return new Http\DataResponse(); |
|
331 | - } |
|
332 | - |
|
333 | - protected function executeAcceptShare(Share\IShare $share) { |
|
334 | - $fileId = (int) $share->getNode()->getId(); |
|
335 | - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
336 | - |
|
337 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
338 | - $event->setApp('files_sharing') |
|
339 | - ->setType('remote_share') |
|
340 | - ->setAffectedUser($this->getCorrectUid($share)) |
|
341 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) |
|
342 | - ->setObject('files', $fileId, $file) |
|
343 | - ->setLink($link); |
|
344 | - \OC::$server->getActivityManager()->publish($event); |
|
345 | - } |
|
346 | - |
|
347 | - /** |
|
348 | - * @NoCSRFRequired |
|
349 | - * @PublicPage |
|
350 | - * |
|
351 | - * decline server-to-server share |
|
352 | - * |
|
353 | - * @param int $id |
|
354 | - * @return Http\DataResponse |
|
355 | - * @throws OCSException |
|
356 | - */ |
|
357 | - public function declineShare($id) { |
|
358 | - |
|
359 | - if (!$this->isS2SEnabled()) { |
|
360 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
361 | - } |
|
362 | - |
|
363 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
364 | - |
|
365 | - try { |
|
366 | - $share = $this->federatedShareProvider->getShareById($id); |
|
367 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
368 | - return new Http\DataResponse(); |
|
369 | - } |
|
370 | - |
|
371 | - if ($this->verifyShare($share, $token)) { |
|
372 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
373 | - list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
374 | - $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
375 | - $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken()); |
|
376 | - } |
|
377 | - $this->executeDeclineShare($share); |
|
378 | - } |
|
379 | - |
|
380 | - return new Http\DataResponse(); |
|
381 | - } |
|
382 | - |
|
383 | - /** |
|
384 | - * delete declined share and create a activity |
|
385 | - * |
|
386 | - * @param Share\IShare $share |
|
387 | - */ |
|
388 | - protected function executeDeclineShare(Share\IShare $share) { |
|
389 | - $this->federatedShareProvider->removeShareFromTable($share); |
|
390 | - $fileId = (int) $share->getNode()->getId(); |
|
391 | - list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
392 | - |
|
393 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
394 | - $event->setApp('files_sharing') |
|
395 | - ->setType('remote_share') |
|
396 | - ->setAffectedUser($this->getCorrectUid($share)) |
|
397 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) |
|
398 | - ->setObject('files', $fileId, $file) |
|
399 | - ->setLink($link); |
|
400 | - \OC::$server->getActivityManager()->publish($event); |
|
401 | - |
|
402 | - } |
|
403 | - |
|
404 | - /** |
|
405 | - * check if we are the initiator or the owner of a re-share and return the correct UID |
|
406 | - * |
|
407 | - * @param Share\IShare $share |
|
408 | - * @return string |
|
409 | - */ |
|
410 | - protected function getCorrectUid(Share\IShare $share) { |
|
411 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
412 | - return $share->getShareOwner(); |
|
413 | - } |
|
414 | - |
|
415 | - return $share->getSharedBy(); |
|
416 | - } |
|
417 | - |
|
418 | - /** |
|
419 | - * @NoCSRFRequired |
|
420 | - * @PublicPage |
|
421 | - * |
|
422 | - * remove server-to-server share if it was unshared by the owner |
|
423 | - * |
|
424 | - * @param int $id |
|
425 | - * @return Http\DataResponse |
|
426 | - * @throws OCSException |
|
427 | - */ |
|
428 | - public function unshare($id) { |
|
429 | - |
|
430 | - if (!$this->isS2SEnabled()) { |
|
431 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
432 | - } |
|
433 | - |
|
434 | - $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
435 | - |
|
436 | - $qb = $this->connection->getQueryBuilder(); |
|
437 | - $qb->select('*') |
|
438 | - ->from('share_external') |
|
439 | - ->where( |
|
440 | - $qb->expr()->andX( |
|
441 | - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
442 | - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
443 | - ) |
|
444 | - ); |
|
445 | - |
|
446 | - $result = $qb->execute(); |
|
447 | - $share = $result->fetch(); |
|
448 | - $result->closeCursor(); |
|
449 | - |
|
450 | - if ($token && $id && !empty($share)) { |
|
451 | - |
|
452 | - $remote = $this->cleanupRemote($share['remote']); |
|
453 | - |
|
454 | - $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); |
|
455 | - $mountpoint = $share['mountpoint']; |
|
456 | - $user = $share['user']; |
|
457 | - |
|
458 | - $qb = $this->connection->getQueryBuilder(); |
|
459 | - $qb->delete('share_external') |
|
460 | - ->where( |
|
461 | - $qb->expr()->andX( |
|
462 | - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
463 | - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
464 | - ) |
|
465 | - ); |
|
466 | - |
|
467 | - $result = $qb->execute(); |
|
468 | - $result->closeCursor(); |
|
469 | - |
|
470 | - if ($share['accepted']) { |
|
471 | - $path = trim($mountpoint, '/'); |
|
472 | - } else { |
|
473 | - $path = trim($share['name'], '/'); |
|
474 | - } |
|
475 | - |
|
476 | - $notificationManager = \OC::$server->getNotificationManager(); |
|
477 | - $notification = $notificationManager->createNotification(); |
|
478 | - $notification->setApp('files_sharing') |
|
479 | - ->setUser($share['user']) |
|
480 | - ->setObject('remote_share', (int)$share['id']); |
|
481 | - $notificationManager->markProcessed($notification); |
|
482 | - |
|
483 | - $event = \OC::$server->getActivityManager()->generateEvent(); |
|
484 | - $event->setApp('files_sharing') |
|
485 | - ->setType('remote_share') |
|
486 | - ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) |
|
487 | - ->setAffectedUser($user) |
|
488 | - ->setObject('remote_share', (int)$share['id'], $path); |
|
489 | - \OC::$server->getActivityManager()->publish($event); |
|
490 | - } |
|
491 | - |
|
492 | - return new Http\DataResponse(); |
|
493 | - } |
|
494 | - |
|
495 | - private function cleanupRemote($remote) { |
|
496 | - $remote = substr($remote, strpos($remote, '://') + 3); |
|
497 | - |
|
498 | - return rtrim($remote, '/'); |
|
499 | - } |
|
500 | - |
|
501 | - |
|
502 | - /** |
|
503 | - * @NoCSRFRequired |
|
504 | - * @PublicPage |
|
505 | - * |
|
506 | - * federated share was revoked, either by the owner or the re-sharer |
|
507 | - * |
|
508 | - * @param int $id |
|
509 | - * @return Http\DataResponse |
|
510 | - * @throws OCSBadRequestException |
|
511 | - */ |
|
512 | - public function revoke($id) { |
|
513 | - $token = $this->request->getParam('token'); |
|
514 | - |
|
515 | - $share = $this->federatedShareProvider->getShareById($id); |
|
516 | - |
|
517 | - if ($this->verifyShare($share, $token)) { |
|
518 | - $this->federatedShareProvider->removeShareFromTable($share); |
|
519 | - return new Http\DataResponse(); |
|
520 | - } |
|
521 | - |
|
522 | - throw new OCSBadRequestException(); |
|
523 | - } |
|
524 | - |
|
525 | - /** |
|
526 | - * get share |
|
527 | - * |
|
528 | - * @param int $id |
|
529 | - * @param string $token |
|
530 | - * @return array|bool |
|
531 | - */ |
|
532 | - protected function getShare($id, $token) { |
|
533 | - $query = $this->connection->getQueryBuilder(); |
|
534 | - $query->select('*')->from($this->shareTable) |
|
535 | - ->where($query->expr()->eq('token', $query->createNamedParameter($token))) |
|
536 | - ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE))) |
|
537 | - ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
538 | - |
|
539 | - $result = $query->execute()->fetchAll(); |
|
540 | - |
|
541 | - if (!empty($result) && isset($result[0])) { |
|
542 | - return $result[0]; |
|
543 | - } |
|
544 | - |
|
545 | - return false; |
|
546 | - } |
|
547 | - |
|
548 | - /** |
|
549 | - * get file |
|
550 | - * |
|
551 | - * @param string $user |
|
552 | - * @param int $fileSource |
|
553 | - * @return array with internal path of the file and a absolute link to it |
|
554 | - */ |
|
555 | - private function getFile($user, $fileSource) { |
|
556 | - \OC_Util::setupFS($user); |
|
557 | - |
|
558 | - try { |
|
559 | - $file = \OC\Files\Filesystem::getPath($fileSource); |
|
560 | - } catch (NotFoundException $e) { |
|
561 | - $file = null; |
|
562 | - } |
|
563 | - $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); |
|
564 | - $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); |
|
565 | - |
|
566 | - return array($file, $link); |
|
567 | - |
|
568 | - } |
|
569 | - |
|
570 | - /** |
|
571 | - * check if server-to-server sharing is enabled |
|
572 | - * |
|
573 | - * @param bool $incoming |
|
574 | - * @return bool |
|
575 | - */ |
|
576 | - private function isS2SEnabled($incoming = false) { |
|
577 | - |
|
578 | - $result = \OCP\App::isEnabled('files_sharing'); |
|
579 | - |
|
580 | - if ($incoming) { |
|
581 | - $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); |
|
582 | - } else { |
|
583 | - $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled(); |
|
584 | - } |
|
585 | - |
|
586 | - return $result; |
|
587 | - } |
|
588 | - |
|
589 | - /** |
|
590 | - * check if we got the right share |
|
591 | - * |
|
592 | - * @param Share\IShare $share |
|
593 | - * @param string $token |
|
594 | - * @return bool |
|
595 | - */ |
|
596 | - protected function verifyShare(Share\IShare $share, $token) { |
|
597 | - if ( |
|
598 | - $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && |
|
599 | - $share->getToken() === $token |
|
600 | - ) { |
|
601 | - return true; |
|
602 | - } |
|
603 | - |
|
604 | - return false; |
|
605 | - } |
|
606 | - |
|
607 | - /** |
|
608 | - * @NoCSRFRequired |
|
609 | - * @PublicPage |
|
610 | - * |
|
611 | - * update share information to keep federated re-shares in sync |
|
612 | - * |
|
613 | - * @param int $id |
|
614 | - * @return Http\DataResponse |
|
615 | - * @throws OCSBadRequestException |
|
616 | - */ |
|
617 | - public function updatePermissions($id) { |
|
618 | - $token = $this->request->getParam('token', null); |
|
619 | - $permissions = $this->request->getParam('permissions', null); |
|
620 | - |
|
621 | - try { |
|
622 | - $share = $this->federatedShareProvider->getShareById($id); |
|
623 | - } catch (Share\Exceptions\ShareNotFound $e) { |
|
624 | - throw new OCSBadRequestException(); |
|
625 | - } |
|
626 | - |
|
627 | - $validPermission = ctype_digit($permissions); |
|
628 | - $validToken = $this->verifyShare($share, $token); |
|
629 | - if ($validPermission && $validToken) { |
|
630 | - $this->updatePermissionsInDatabase($share, (int)$permissions); |
|
631 | - } else { |
|
632 | - throw new OCSBadRequestException(); |
|
633 | - } |
|
634 | - |
|
635 | - return new Http\DataResponse(); |
|
636 | - } |
|
637 | - |
|
638 | - /** |
|
639 | - * update permissions in database |
|
640 | - * |
|
641 | - * @param IShare $share |
|
642 | - * @param int $permissions |
|
643 | - */ |
|
644 | - protected function updatePermissionsInDatabase(IShare $share, $permissions) { |
|
645 | - $query = $this->connection->getQueryBuilder(); |
|
646 | - $query->update('share') |
|
647 | - ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) |
|
648 | - ->set('permissions', $query->createNamedParameter($permissions)) |
|
649 | - ->execute(); |
|
650 | - } |
|
651 | - |
|
652 | - /** |
|
653 | - * @NoCSRFRequired |
|
654 | - * @PublicPage |
|
655 | - * |
|
656 | - * change the owner of a server-to-server share |
|
657 | - * |
|
658 | - * @param int $id |
|
659 | - * @return Http\DataResponse |
|
660 | - * @throws \InvalidArgumentException |
|
661 | - * @throws OCSException |
|
662 | - */ |
|
663 | - public function move($id) { |
|
664 | - |
|
665 | - if (!$this->isS2SEnabled()) { |
|
666 | - throw new OCSException('Server does not support federated cloud sharing', 503); |
|
667 | - } |
|
668 | - |
|
669 | - $token = $this->request->getParam('token'); |
|
670 | - $remote = $this->request->getParam('remote'); |
|
671 | - $newRemoteId = $this->request->getParam('remote_id', $id); |
|
672 | - $cloudId = $this->cloudIdManager->resolveCloudId($remote); |
|
673 | - |
|
674 | - $qb = $this->connection->getQueryBuilder(); |
|
675 | - $query = $qb->update('share_external') |
|
676 | - ->set('remote', $qb->createNamedParameter($cloudId->getRemote())) |
|
677 | - ->set('owner', $qb->createNamedParameter($cloudId->getUser())) |
|
678 | - ->set('remote_id', $qb->createNamedParameter($newRemoteId)) |
|
679 | - ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id))) |
|
680 | - ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token))); |
|
681 | - $affected = $query->execute(); |
|
682 | - |
|
683 | - if ($affected > 0) { |
|
684 | - return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); |
|
685 | - } else { |
|
686 | - throw new OCSBadRequestException('Share not found or token invalid'); |
|
687 | - } |
|
688 | - } |
|
54 | + /** @var FederatedShareProvider */ |
|
55 | + private $federatedShareProvider; |
|
56 | + |
|
57 | + /** @var IDBConnection */ |
|
58 | + private $connection; |
|
59 | + |
|
60 | + /** @var Share\IManager */ |
|
61 | + private $shareManager; |
|
62 | + |
|
63 | + /** @var Notifications */ |
|
64 | + private $notifications; |
|
65 | + |
|
66 | + /** @var AddressHandler */ |
|
67 | + private $addressHandler; |
|
68 | + |
|
69 | + /** @var IUserManager */ |
|
70 | + private $userManager; |
|
71 | + |
|
72 | + /** @var string */ |
|
73 | + private $shareTable = 'share'; |
|
74 | + |
|
75 | + /** @var ICloudIdManager */ |
|
76 | + private $cloudIdManager; |
|
77 | + |
|
78 | + /** @var ILogger */ |
|
79 | + private $logger; |
|
80 | + |
|
81 | + /** |
|
82 | + * Server2Server constructor. |
|
83 | + * |
|
84 | + * @param string $appName |
|
85 | + * @param IRequest $request |
|
86 | + * @param FederatedShareProvider $federatedShareProvider |
|
87 | + * @param IDBConnection $connection |
|
88 | + * @param Share\IManager $shareManager |
|
89 | + * @param Notifications $notifications |
|
90 | + * @param AddressHandler $addressHandler |
|
91 | + * @param IUserManager $userManager |
|
92 | + * @param ICloudIdManager $cloudIdManager |
|
93 | + */ |
|
94 | + public function __construct($appName, |
|
95 | + IRequest $request, |
|
96 | + FederatedShareProvider $federatedShareProvider, |
|
97 | + IDBConnection $connection, |
|
98 | + Share\IManager $shareManager, |
|
99 | + Notifications $notifications, |
|
100 | + AddressHandler $addressHandler, |
|
101 | + IUserManager $userManager, |
|
102 | + ICloudIdManager $cloudIdManager, |
|
103 | + ILogger $logger |
|
104 | + ) { |
|
105 | + parent::__construct($appName, $request); |
|
106 | + |
|
107 | + $this->federatedShareProvider = $federatedShareProvider; |
|
108 | + $this->connection = $connection; |
|
109 | + $this->shareManager = $shareManager; |
|
110 | + $this->notifications = $notifications; |
|
111 | + $this->addressHandler = $addressHandler; |
|
112 | + $this->userManager = $userManager; |
|
113 | + $this->cloudIdManager = $cloudIdManager; |
|
114 | + $this->logger = $logger; |
|
115 | + } |
|
116 | + |
|
117 | + /** |
|
118 | + * @NoCSRFRequired |
|
119 | + * @PublicPage |
|
120 | + * |
|
121 | + * create a new share |
|
122 | + * |
|
123 | + * @return Http\DataResponse |
|
124 | + * @throws OCSException |
|
125 | + */ |
|
126 | + public function createShare() { |
|
127 | + |
|
128 | + if (!$this->isS2SEnabled(true)) { |
|
129 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
130 | + } |
|
131 | + |
|
132 | + $remote = isset($_POST['remote']) ? $_POST['remote'] : null; |
|
133 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
134 | + $name = isset($_POST['name']) ? $_POST['name'] : null; |
|
135 | + $owner = isset($_POST['owner']) ? $_POST['owner'] : null; |
|
136 | + $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; |
|
137 | + $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; |
|
138 | + $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; |
|
139 | + $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; |
|
140 | + $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; |
|
141 | + |
|
142 | + if ($remote && $token && $name && $owner && $remoteId && $shareWith) { |
|
143 | + |
|
144 | + if (!\OCP\Util::isValidFileName($name)) { |
|
145 | + throw new OCSException('The mountpoint name contains invalid characters.', 400); |
|
146 | + } |
|
147 | + |
|
148 | + // FIXME this should be a method in the user management instead |
|
149 | + $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); |
|
150 | + \OCP\Util::emitHook( |
|
151 | + '\OCA\Files_Sharing\API\Server2Server', |
|
152 | + 'preLoginNameUsedAsUserName', |
|
153 | + array('uid' => &$shareWith) |
|
154 | + ); |
|
155 | + $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); |
|
156 | + |
|
157 | + if (!\OC::$server->getUserManager()->userExists($shareWith)) { |
|
158 | + throw new OCSException('User does not exists', 400); |
|
159 | + } |
|
160 | + |
|
161 | + \OC_Util::setupFS($shareWith); |
|
162 | + |
|
163 | + $externalManager = new \OCA\Files_Sharing\External\Manager( |
|
164 | + \OC::$server->getDatabaseConnection(), |
|
165 | + \OC\Files\Filesystem::getMountManager(), |
|
166 | + \OC\Files\Filesystem::getLoader(), |
|
167 | + \OC::$server->getHTTPClientService(), |
|
168 | + \OC::$server->getNotificationManager(), |
|
169 | + \OC::$server->query(\OCP\OCS\IDiscoveryService::class), |
|
170 | + $shareWith |
|
171 | + ); |
|
172 | + |
|
173 | + try { |
|
174 | + $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId); |
|
175 | + $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); |
|
176 | + |
|
177 | + if ($ownerFederatedId === null) { |
|
178 | + $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId(); |
|
179 | + } |
|
180 | + // if the owner of the share and the initiator are the same user |
|
181 | + // we also complete the federated share ID for the initiator |
|
182 | + if ($sharedByFederatedId === null && $owner === $sharedBy) { |
|
183 | + $sharedByFederatedId = $ownerFederatedId; |
|
184 | + } |
|
185 | + |
|
186 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
187 | + $event->setApp('files_sharing') |
|
188 | + ->setType('remote_share') |
|
189 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) |
|
190 | + ->setAffectedUser($shareWith) |
|
191 | + ->setObject('remote_share', (int)$shareId, $name); |
|
192 | + \OC::$server->getActivityManager()->publish($event); |
|
193 | + |
|
194 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
195 | + |
|
196 | + $notificationManager = \OC::$server->getNotificationManager(); |
|
197 | + $notification = $notificationManager->createNotification(); |
|
198 | + $notification->setApp('files_sharing') |
|
199 | + ->setUser($shareWith) |
|
200 | + ->setDateTime(new \DateTime()) |
|
201 | + ->setObject('remote_share', $shareId) |
|
202 | + ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]); |
|
203 | + |
|
204 | + $declineAction = $notification->createAction(); |
|
205 | + $declineAction->setLabel('decline') |
|
206 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); |
|
207 | + $notification->addAction($declineAction); |
|
208 | + |
|
209 | + $acceptAction = $notification->createAction(); |
|
210 | + $acceptAction->setLabel('accept') |
|
211 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); |
|
212 | + $notification->addAction($acceptAction); |
|
213 | + |
|
214 | + $notificationManager->notify($notification); |
|
215 | + |
|
216 | + return new Http\DataResponse(); |
|
217 | + } catch (\Exception $e) { |
|
218 | + $this->logger->logException($e, [ |
|
219 | + 'message' => 'Server can not add remote share.', |
|
220 | + 'level' => ILogger::ERROR, |
|
221 | + 'app' => 'files_sharing' |
|
222 | + ]); |
|
223 | + throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); |
|
224 | + } |
|
225 | + } |
|
226 | + |
|
227 | + throw new OCSException('server can not add remote share, missing parameter', 400); |
|
228 | + } |
|
229 | + |
|
230 | + /** |
|
231 | + * @NoCSRFRequired |
|
232 | + * @PublicPage |
|
233 | + * |
|
234 | + * create re-share on behalf of another user |
|
235 | + * |
|
236 | + * @param int $id |
|
237 | + * @return Http\DataResponse |
|
238 | + * @throws OCSBadRequestException |
|
239 | + * @throws OCSForbiddenException |
|
240 | + * @throws OCSNotFoundException |
|
241 | + */ |
|
242 | + public function reShare($id) { |
|
243 | + |
|
244 | + $token = $this->request->getParam('token', null); |
|
245 | + $shareWith = $this->request->getParam('shareWith', null); |
|
246 | + $permission = (int)$this->request->getParam('permission', null); |
|
247 | + $remoteId = (int)$this->request->getParam('remoteId', null); |
|
248 | + |
|
249 | + if ($id === null || |
|
250 | + $token === null || |
|
251 | + $shareWith === null || |
|
252 | + $permission === null || |
|
253 | + $remoteId === null |
|
254 | + ) { |
|
255 | + throw new OCSBadRequestException(); |
|
256 | + } |
|
257 | + |
|
258 | + try { |
|
259 | + $share = $this->federatedShareProvider->getShareById($id); |
|
260 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
261 | + throw new OCSNotFoundException(); |
|
262 | + } |
|
263 | + |
|
264 | + // don't allow to share a file back to the owner |
|
265 | + list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith); |
|
266 | + $owner = $share->getShareOwner(); |
|
267 | + $currentServer = $this->addressHandler->generateRemoteURL(); |
|
268 | + if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) { |
|
269 | + throw new OCSForbiddenException(); |
|
270 | + } |
|
271 | + |
|
272 | + if ($this->verifyShare($share, $token)) { |
|
273 | + |
|
274 | + // check if re-sharing is allowed |
|
275 | + if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) { |
|
276 | + $share->setPermissions($share->getPermissions() & $permission); |
|
277 | + // the recipient of the initial share is now the initiator for the re-share |
|
278 | + $share->setSharedBy($share->getSharedWith()); |
|
279 | + $share->setSharedWith($shareWith); |
|
280 | + try { |
|
281 | + $result = $this->federatedShareProvider->create($share); |
|
282 | + $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); |
|
283 | + return new Http\DataResponse([ |
|
284 | + 'token' => $result->getToken(), |
|
285 | + 'remoteId' => $result->getId() |
|
286 | + ]); |
|
287 | + } catch (\Exception $e) { |
|
288 | + throw new OCSBadRequestException(); |
|
289 | + } |
|
290 | + } else { |
|
291 | + throw new OCSForbiddenException(); |
|
292 | + } |
|
293 | + } |
|
294 | + throw new OCSBadRequestException(); |
|
295 | + } |
|
296 | + |
|
297 | + /** |
|
298 | + * @NoCSRFRequired |
|
299 | + * @PublicPage |
|
300 | + * |
|
301 | + * accept server-to-server share |
|
302 | + * |
|
303 | + * @param int $id |
|
304 | + * @return Http\DataResponse |
|
305 | + * @throws OCSException |
|
306 | + */ |
|
307 | + public function acceptShare($id) { |
|
308 | + |
|
309 | + if (!$this->isS2SEnabled()) { |
|
310 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
311 | + } |
|
312 | + |
|
313 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
314 | + |
|
315 | + try { |
|
316 | + $share = $this->federatedShareProvider->getShareById($id); |
|
317 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
318 | + return new Http\DataResponse(); |
|
319 | + } |
|
320 | + |
|
321 | + if ($this->verifyShare($share, $token)) { |
|
322 | + $this->executeAcceptShare($share); |
|
323 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
324 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
325 | + $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
326 | + $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken()); |
|
327 | + } |
|
328 | + } |
|
329 | + |
|
330 | + return new Http\DataResponse(); |
|
331 | + } |
|
332 | + |
|
333 | + protected function executeAcceptShare(Share\IShare $share) { |
|
334 | + $fileId = (int) $share->getNode()->getId(); |
|
335 | + list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
336 | + |
|
337 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
338 | + $event->setApp('files_sharing') |
|
339 | + ->setType('remote_share') |
|
340 | + ->setAffectedUser($this->getCorrectUid($share)) |
|
341 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) |
|
342 | + ->setObject('files', $fileId, $file) |
|
343 | + ->setLink($link); |
|
344 | + \OC::$server->getActivityManager()->publish($event); |
|
345 | + } |
|
346 | + |
|
347 | + /** |
|
348 | + * @NoCSRFRequired |
|
349 | + * @PublicPage |
|
350 | + * |
|
351 | + * decline server-to-server share |
|
352 | + * |
|
353 | + * @param int $id |
|
354 | + * @return Http\DataResponse |
|
355 | + * @throws OCSException |
|
356 | + */ |
|
357 | + public function declineShare($id) { |
|
358 | + |
|
359 | + if (!$this->isS2SEnabled()) { |
|
360 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
361 | + } |
|
362 | + |
|
363 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
364 | + |
|
365 | + try { |
|
366 | + $share = $this->federatedShareProvider->getShareById($id); |
|
367 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
368 | + return new Http\DataResponse(); |
|
369 | + } |
|
370 | + |
|
371 | + if ($this->verifyShare($share, $token)) { |
|
372 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
373 | + list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy()); |
|
374 | + $remoteId = $this->federatedShareProvider->getRemoteId($share); |
|
375 | + $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken()); |
|
376 | + } |
|
377 | + $this->executeDeclineShare($share); |
|
378 | + } |
|
379 | + |
|
380 | + return new Http\DataResponse(); |
|
381 | + } |
|
382 | + |
|
383 | + /** |
|
384 | + * delete declined share and create a activity |
|
385 | + * |
|
386 | + * @param Share\IShare $share |
|
387 | + */ |
|
388 | + protected function executeDeclineShare(Share\IShare $share) { |
|
389 | + $this->federatedShareProvider->removeShareFromTable($share); |
|
390 | + $fileId = (int) $share->getNode()->getId(); |
|
391 | + list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId); |
|
392 | + |
|
393 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
394 | + $event->setApp('files_sharing') |
|
395 | + ->setType('remote_share') |
|
396 | + ->setAffectedUser($this->getCorrectUid($share)) |
|
397 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) |
|
398 | + ->setObject('files', $fileId, $file) |
|
399 | + ->setLink($link); |
|
400 | + \OC::$server->getActivityManager()->publish($event); |
|
401 | + |
|
402 | + } |
|
403 | + |
|
404 | + /** |
|
405 | + * check if we are the initiator or the owner of a re-share and return the correct UID |
|
406 | + * |
|
407 | + * @param Share\IShare $share |
|
408 | + * @return string |
|
409 | + */ |
|
410 | + protected function getCorrectUid(Share\IShare $share) { |
|
411 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
412 | + return $share->getShareOwner(); |
|
413 | + } |
|
414 | + |
|
415 | + return $share->getSharedBy(); |
|
416 | + } |
|
417 | + |
|
418 | + /** |
|
419 | + * @NoCSRFRequired |
|
420 | + * @PublicPage |
|
421 | + * |
|
422 | + * remove server-to-server share if it was unshared by the owner |
|
423 | + * |
|
424 | + * @param int $id |
|
425 | + * @return Http\DataResponse |
|
426 | + * @throws OCSException |
|
427 | + */ |
|
428 | + public function unshare($id) { |
|
429 | + |
|
430 | + if (!$this->isS2SEnabled()) { |
|
431 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
432 | + } |
|
433 | + |
|
434 | + $token = isset($_POST['token']) ? $_POST['token'] : null; |
|
435 | + |
|
436 | + $qb = $this->connection->getQueryBuilder(); |
|
437 | + $qb->select('*') |
|
438 | + ->from('share_external') |
|
439 | + ->where( |
|
440 | + $qb->expr()->andX( |
|
441 | + $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
442 | + $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
443 | + ) |
|
444 | + ); |
|
445 | + |
|
446 | + $result = $qb->execute(); |
|
447 | + $share = $result->fetch(); |
|
448 | + $result->closeCursor(); |
|
449 | + |
|
450 | + if ($token && $id && !empty($share)) { |
|
451 | + |
|
452 | + $remote = $this->cleanupRemote($share['remote']); |
|
453 | + |
|
454 | + $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); |
|
455 | + $mountpoint = $share['mountpoint']; |
|
456 | + $user = $share['user']; |
|
457 | + |
|
458 | + $qb = $this->connection->getQueryBuilder(); |
|
459 | + $qb->delete('share_external') |
|
460 | + ->where( |
|
461 | + $qb->expr()->andX( |
|
462 | + $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), |
|
463 | + $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) |
|
464 | + ) |
|
465 | + ); |
|
466 | + |
|
467 | + $result = $qb->execute(); |
|
468 | + $result->closeCursor(); |
|
469 | + |
|
470 | + if ($share['accepted']) { |
|
471 | + $path = trim($mountpoint, '/'); |
|
472 | + } else { |
|
473 | + $path = trim($share['name'], '/'); |
|
474 | + } |
|
475 | + |
|
476 | + $notificationManager = \OC::$server->getNotificationManager(); |
|
477 | + $notification = $notificationManager->createNotification(); |
|
478 | + $notification->setApp('files_sharing') |
|
479 | + ->setUser($share['user']) |
|
480 | + ->setObject('remote_share', (int)$share['id']); |
|
481 | + $notificationManager->markProcessed($notification); |
|
482 | + |
|
483 | + $event = \OC::$server->getActivityManager()->generateEvent(); |
|
484 | + $event->setApp('files_sharing') |
|
485 | + ->setType('remote_share') |
|
486 | + ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) |
|
487 | + ->setAffectedUser($user) |
|
488 | + ->setObject('remote_share', (int)$share['id'], $path); |
|
489 | + \OC::$server->getActivityManager()->publish($event); |
|
490 | + } |
|
491 | + |
|
492 | + return new Http\DataResponse(); |
|
493 | + } |
|
494 | + |
|
495 | + private function cleanupRemote($remote) { |
|
496 | + $remote = substr($remote, strpos($remote, '://') + 3); |
|
497 | + |
|
498 | + return rtrim($remote, '/'); |
|
499 | + } |
|
500 | + |
|
501 | + |
|
502 | + /** |
|
503 | + * @NoCSRFRequired |
|
504 | + * @PublicPage |
|
505 | + * |
|
506 | + * federated share was revoked, either by the owner or the re-sharer |
|
507 | + * |
|
508 | + * @param int $id |
|
509 | + * @return Http\DataResponse |
|
510 | + * @throws OCSBadRequestException |
|
511 | + */ |
|
512 | + public function revoke($id) { |
|
513 | + $token = $this->request->getParam('token'); |
|
514 | + |
|
515 | + $share = $this->federatedShareProvider->getShareById($id); |
|
516 | + |
|
517 | + if ($this->verifyShare($share, $token)) { |
|
518 | + $this->federatedShareProvider->removeShareFromTable($share); |
|
519 | + return new Http\DataResponse(); |
|
520 | + } |
|
521 | + |
|
522 | + throw new OCSBadRequestException(); |
|
523 | + } |
|
524 | + |
|
525 | + /** |
|
526 | + * get share |
|
527 | + * |
|
528 | + * @param int $id |
|
529 | + * @param string $token |
|
530 | + * @return array|bool |
|
531 | + */ |
|
532 | + protected function getShare($id, $token) { |
|
533 | + $query = $this->connection->getQueryBuilder(); |
|
534 | + $query->select('*')->from($this->shareTable) |
|
535 | + ->where($query->expr()->eq('token', $query->createNamedParameter($token))) |
|
536 | + ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE))) |
|
537 | + ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id))); |
|
538 | + |
|
539 | + $result = $query->execute()->fetchAll(); |
|
540 | + |
|
541 | + if (!empty($result) && isset($result[0])) { |
|
542 | + return $result[0]; |
|
543 | + } |
|
544 | + |
|
545 | + return false; |
|
546 | + } |
|
547 | + |
|
548 | + /** |
|
549 | + * get file |
|
550 | + * |
|
551 | + * @param string $user |
|
552 | + * @param int $fileSource |
|
553 | + * @return array with internal path of the file and a absolute link to it |
|
554 | + */ |
|
555 | + private function getFile($user, $fileSource) { |
|
556 | + \OC_Util::setupFS($user); |
|
557 | + |
|
558 | + try { |
|
559 | + $file = \OC\Files\Filesystem::getPath($fileSource); |
|
560 | + } catch (NotFoundException $e) { |
|
561 | + $file = null; |
|
562 | + } |
|
563 | + $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file); |
|
564 | + $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); |
|
565 | + |
|
566 | + return array($file, $link); |
|
567 | + |
|
568 | + } |
|
569 | + |
|
570 | + /** |
|
571 | + * check if server-to-server sharing is enabled |
|
572 | + * |
|
573 | + * @param bool $incoming |
|
574 | + * @return bool |
|
575 | + */ |
|
576 | + private function isS2SEnabled($incoming = false) { |
|
577 | + |
|
578 | + $result = \OCP\App::isEnabled('files_sharing'); |
|
579 | + |
|
580 | + if ($incoming) { |
|
581 | + $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); |
|
582 | + } else { |
|
583 | + $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled(); |
|
584 | + } |
|
585 | + |
|
586 | + return $result; |
|
587 | + } |
|
588 | + |
|
589 | + /** |
|
590 | + * check if we got the right share |
|
591 | + * |
|
592 | + * @param Share\IShare $share |
|
593 | + * @param string $token |
|
594 | + * @return bool |
|
595 | + */ |
|
596 | + protected function verifyShare(Share\IShare $share, $token) { |
|
597 | + if ( |
|
598 | + $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE && |
|
599 | + $share->getToken() === $token |
|
600 | + ) { |
|
601 | + return true; |
|
602 | + } |
|
603 | + |
|
604 | + return false; |
|
605 | + } |
|
606 | + |
|
607 | + /** |
|
608 | + * @NoCSRFRequired |
|
609 | + * @PublicPage |
|
610 | + * |
|
611 | + * update share information to keep federated re-shares in sync |
|
612 | + * |
|
613 | + * @param int $id |
|
614 | + * @return Http\DataResponse |
|
615 | + * @throws OCSBadRequestException |
|
616 | + */ |
|
617 | + public function updatePermissions($id) { |
|
618 | + $token = $this->request->getParam('token', null); |
|
619 | + $permissions = $this->request->getParam('permissions', null); |
|
620 | + |
|
621 | + try { |
|
622 | + $share = $this->federatedShareProvider->getShareById($id); |
|
623 | + } catch (Share\Exceptions\ShareNotFound $e) { |
|
624 | + throw new OCSBadRequestException(); |
|
625 | + } |
|
626 | + |
|
627 | + $validPermission = ctype_digit($permissions); |
|
628 | + $validToken = $this->verifyShare($share, $token); |
|
629 | + if ($validPermission && $validToken) { |
|
630 | + $this->updatePermissionsInDatabase($share, (int)$permissions); |
|
631 | + } else { |
|
632 | + throw new OCSBadRequestException(); |
|
633 | + } |
|
634 | + |
|
635 | + return new Http\DataResponse(); |
|
636 | + } |
|
637 | + |
|
638 | + /** |
|
639 | + * update permissions in database |
|
640 | + * |
|
641 | + * @param IShare $share |
|
642 | + * @param int $permissions |
|
643 | + */ |
|
644 | + protected function updatePermissionsInDatabase(IShare $share, $permissions) { |
|
645 | + $query = $this->connection->getQueryBuilder(); |
|
646 | + $query->update('share') |
|
647 | + ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) |
|
648 | + ->set('permissions', $query->createNamedParameter($permissions)) |
|
649 | + ->execute(); |
|
650 | + } |
|
651 | + |
|
652 | + /** |
|
653 | + * @NoCSRFRequired |
|
654 | + * @PublicPage |
|
655 | + * |
|
656 | + * change the owner of a server-to-server share |
|
657 | + * |
|
658 | + * @param int $id |
|
659 | + * @return Http\DataResponse |
|
660 | + * @throws \InvalidArgumentException |
|
661 | + * @throws OCSException |
|
662 | + */ |
|
663 | + public function move($id) { |
|
664 | + |
|
665 | + if (!$this->isS2SEnabled()) { |
|
666 | + throw new OCSException('Server does not support federated cloud sharing', 503); |
|
667 | + } |
|
668 | + |
|
669 | + $token = $this->request->getParam('token'); |
|
670 | + $remote = $this->request->getParam('remote'); |
|
671 | + $newRemoteId = $this->request->getParam('remote_id', $id); |
|
672 | + $cloudId = $this->cloudIdManager->resolveCloudId($remote); |
|
673 | + |
|
674 | + $qb = $this->connection->getQueryBuilder(); |
|
675 | + $query = $qb->update('share_external') |
|
676 | + ->set('remote', $qb->createNamedParameter($cloudId->getRemote())) |
|
677 | + ->set('owner', $qb->createNamedParameter($cloudId->getUser())) |
|
678 | + ->set('remote_id', $qb->createNamedParameter($newRemoteId)) |
|
679 | + ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id))) |
|
680 | + ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token))); |
|
681 | + $affected = $query->execute(); |
|
682 | + |
|
683 | + if ($affected > 0) { |
|
684 | + return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); |
|
685 | + } else { |
|
686 | + throw new OCSBadRequestException('Share not found or token invalid'); |
|
687 | + } |
|
688 | + } |
|
689 | 689 | } |
@@ -135,7 +135,7 @@ discard block |
||
135 | 135 | $owner = isset($_POST['owner']) ? $_POST['owner'] : null; |
136 | 136 | $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null; |
137 | 137 | $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null; |
138 | - $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null; |
|
138 | + $remoteId = isset($_POST['remoteId']) ? (int) $_POST['remoteId'] : null; |
|
139 | 139 | $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null; |
140 | 140 | $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null; |
141 | 141 | |
@@ -146,13 +146,13 @@ discard block |
||
146 | 146 | } |
147 | 147 | |
148 | 148 | // FIXME this should be a method in the user management instead |
149 | - $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); |
|
149 | + $this->logger->debug('shareWith before, '.$shareWith, ['app' => 'files_sharing']); |
|
150 | 150 | \OCP\Util::emitHook( |
151 | 151 | '\OCA\Files_Sharing\API\Server2Server', |
152 | 152 | 'preLoginNameUsedAsUserName', |
153 | 153 | array('uid' => &$shareWith) |
154 | 154 | ); |
155 | - $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); |
|
155 | + $this->logger->debug('shareWith after, '.$shareWith, ['app' => 'files_sharing']); |
|
156 | 156 | |
157 | 157 | if (!\OC::$server->getUserManager()->userExists($shareWith)) { |
158 | 158 | throw new OCSException('User does not exists', 400); |
@@ -188,7 +188,7 @@ discard block |
||
188 | 188 | ->setType('remote_share') |
189 | 189 | ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')]) |
190 | 190 | ->setAffectedUser($shareWith) |
191 | - ->setObject('remote_share', (int)$shareId, $name); |
|
191 | + ->setObject('remote_share', (int) $shareId, $name); |
|
192 | 192 | \OC::$server->getActivityManager()->publish($event); |
193 | 193 | |
194 | 194 | $urlGenerator = \OC::$server->getURLGenerator(); |
@@ -203,12 +203,12 @@ discard block |
||
203 | 203 | |
204 | 204 | $declineAction = $notification->createAction(); |
205 | 205 | $declineAction->setLabel('decline') |
206 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE'); |
|
206 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/'.$shareId)), 'DELETE'); |
|
207 | 207 | $notification->addAction($declineAction); |
208 | 208 | |
209 | 209 | $acceptAction = $notification->createAction(); |
210 | 210 | $acceptAction->setLabel('accept') |
211 | - ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST'); |
|
211 | + ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/'.$shareId)), 'POST'); |
|
212 | 212 | $notification->addAction($acceptAction); |
213 | 213 | |
214 | 214 | $notificationManager->notify($notification); |
@@ -220,7 +220,7 @@ discard block |
||
220 | 220 | 'level' => ILogger::ERROR, |
221 | 221 | 'app' => 'files_sharing' |
222 | 222 | ]); |
223 | - throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); |
|
223 | + throw new OCSException('internal server error, was not able to add share from '.$remote, 500); |
|
224 | 224 | } |
225 | 225 | } |
226 | 226 | |
@@ -243,8 +243,8 @@ discard block |
||
243 | 243 | |
244 | 244 | $token = $this->request->getParam('token', null); |
245 | 245 | $shareWith = $this->request->getParam('shareWith', null); |
246 | - $permission = (int)$this->request->getParam('permission', null); |
|
247 | - $remoteId = (int)$this->request->getParam('remoteId', null); |
|
246 | + $permission = (int) $this->request->getParam('permission', null); |
|
247 | + $remoteId = (int) $this->request->getParam('remoteId', null); |
|
248 | 248 | |
249 | 249 | if ($id === null || |
250 | 250 | $token === null || |
@@ -279,7 +279,7 @@ discard block |
||
279 | 279 | $share->setSharedWith($shareWith); |
280 | 280 | try { |
281 | 281 | $result = $this->federatedShareProvider->create($share); |
282 | - $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); |
|
282 | + $this->federatedShareProvider->storeRemoteId((int) $result->getId(), $remoteId); |
|
283 | 283 | return new Http\DataResponse([ |
284 | 284 | 'token' => $result->getToken(), |
285 | 285 | 'remoteId' => $result->getId() |
@@ -477,7 +477,7 @@ discard block |
||
477 | 477 | $notification = $notificationManager->createNotification(); |
478 | 478 | $notification->setApp('files_sharing') |
479 | 479 | ->setUser($share['user']) |
480 | - ->setObject('remote_share', (int)$share['id']); |
|
480 | + ->setObject('remote_share', (int) $share['id']); |
|
481 | 481 | $notificationManager->markProcessed($notification); |
482 | 482 | |
483 | 483 | $event = \OC::$server->getActivityManager()->generateEvent(); |
@@ -485,7 +485,7 @@ discard block |
||
485 | 485 | ->setType('remote_share') |
486 | 486 | ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path]) |
487 | 487 | ->setAffectedUser($user) |
488 | - ->setObject('remote_share', (int)$share['id'], $path); |
|
488 | + ->setObject('remote_share', (int) $share['id'], $path); |
|
489 | 489 | \OC::$server->getActivityManager()->publish($event); |
490 | 490 | } |
491 | 491 | |
@@ -627,7 +627,7 @@ discard block |
||
627 | 627 | $validPermission = ctype_digit($permissions); |
628 | 628 | $validToken = $this->verifyShare($share, $token); |
629 | 629 | if ($validPermission && $validToken) { |
630 | - $this->updatePermissionsInDatabase($share, (int)$permissions); |
|
630 | + $this->updatePermissionsInDatabase($share, (int) $permissions); |
|
631 | 631 | } else { |
632 | 632 | throw new OCSBadRequestException(); |
633 | 633 | } |
@@ -53,1036 +53,1036 @@ |
||
53 | 53 | */ |
54 | 54 | class ShareByMailProvider implements IShareProvider { |
55 | 55 | |
56 | - /** @var IDBConnection */ |
|
57 | - private $dbConnection; |
|
58 | - |
|
59 | - /** @var ILogger */ |
|
60 | - private $logger; |
|
61 | - |
|
62 | - /** @var ISecureRandom */ |
|
63 | - private $secureRandom; |
|
64 | - |
|
65 | - /** @var IUserManager */ |
|
66 | - private $userManager; |
|
67 | - |
|
68 | - /** @var IRootFolder */ |
|
69 | - private $rootFolder; |
|
70 | - |
|
71 | - /** @var IL10N */ |
|
72 | - private $l; |
|
73 | - |
|
74 | - /** @var IMailer */ |
|
75 | - private $mailer; |
|
76 | - |
|
77 | - /** @var IURLGenerator */ |
|
78 | - private $urlGenerator; |
|
79 | - |
|
80 | - /** @var IManager */ |
|
81 | - private $activityManager; |
|
82 | - |
|
83 | - /** @var SettingsManager */ |
|
84 | - private $settingsManager; |
|
85 | - |
|
86 | - /** @var Defaults */ |
|
87 | - private $defaults; |
|
88 | - |
|
89 | - /** @var IHasher */ |
|
90 | - private $hasher; |
|
91 | - |
|
92 | - /** @var CapabilitiesManager */ |
|
93 | - private $capabilitiesManager; |
|
94 | - |
|
95 | - /** |
|
96 | - * Return the identifier of this provider. |
|
97 | - * |
|
98 | - * @return string Containing only [a-zA-Z0-9] |
|
99 | - */ |
|
100 | - public function identifier() { |
|
101 | - return 'ocMailShare'; |
|
102 | - } |
|
103 | - |
|
104 | - /** |
|
105 | - * DefaultShareProvider constructor. |
|
106 | - * |
|
107 | - * @param IDBConnection $connection |
|
108 | - * @param ISecureRandom $secureRandom |
|
109 | - * @param IUserManager $userManager |
|
110 | - * @param IRootFolder $rootFolder |
|
111 | - * @param IL10N $l |
|
112 | - * @param ILogger $logger |
|
113 | - * @param IMailer $mailer |
|
114 | - * @param IURLGenerator $urlGenerator |
|
115 | - * @param IManager $activityManager |
|
116 | - * @param SettingsManager $settingsManager |
|
117 | - * @param Defaults $defaults |
|
118 | - * @param IHasher $hasher |
|
119 | - * @param CapabilitiesManager $capabilitiesManager |
|
120 | - */ |
|
121 | - public function __construct( |
|
122 | - IDBConnection $connection, |
|
123 | - ISecureRandom $secureRandom, |
|
124 | - IUserManager $userManager, |
|
125 | - IRootFolder $rootFolder, |
|
126 | - IL10N $l, |
|
127 | - ILogger $logger, |
|
128 | - IMailer $mailer, |
|
129 | - IURLGenerator $urlGenerator, |
|
130 | - IManager $activityManager, |
|
131 | - SettingsManager $settingsManager, |
|
132 | - Defaults $defaults, |
|
133 | - IHasher $hasher, |
|
134 | - CapabilitiesManager $capabilitiesManager |
|
135 | - ) { |
|
136 | - $this->dbConnection = $connection; |
|
137 | - $this->secureRandom = $secureRandom; |
|
138 | - $this->userManager = $userManager; |
|
139 | - $this->rootFolder = $rootFolder; |
|
140 | - $this->l = $l; |
|
141 | - $this->logger = $logger; |
|
142 | - $this->mailer = $mailer; |
|
143 | - $this->urlGenerator = $urlGenerator; |
|
144 | - $this->activityManager = $activityManager; |
|
145 | - $this->settingsManager = $settingsManager; |
|
146 | - $this->defaults = $defaults; |
|
147 | - $this->hasher = $hasher; |
|
148 | - $this->capabilitiesManager = $capabilitiesManager; |
|
149 | - } |
|
150 | - |
|
151 | - /** |
|
152 | - * Share a path |
|
153 | - * |
|
154 | - * @param IShare $share |
|
155 | - * @return IShare The share object |
|
156 | - * @throws ShareNotFound |
|
157 | - * @throws \Exception |
|
158 | - */ |
|
159 | - public function create(IShare $share) { |
|
160 | - |
|
161 | - $shareWith = $share->getSharedWith(); |
|
162 | - /* |
|
56 | + /** @var IDBConnection */ |
|
57 | + private $dbConnection; |
|
58 | + |
|
59 | + /** @var ILogger */ |
|
60 | + private $logger; |
|
61 | + |
|
62 | + /** @var ISecureRandom */ |
|
63 | + private $secureRandom; |
|
64 | + |
|
65 | + /** @var IUserManager */ |
|
66 | + private $userManager; |
|
67 | + |
|
68 | + /** @var IRootFolder */ |
|
69 | + private $rootFolder; |
|
70 | + |
|
71 | + /** @var IL10N */ |
|
72 | + private $l; |
|
73 | + |
|
74 | + /** @var IMailer */ |
|
75 | + private $mailer; |
|
76 | + |
|
77 | + /** @var IURLGenerator */ |
|
78 | + private $urlGenerator; |
|
79 | + |
|
80 | + /** @var IManager */ |
|
81 | + private $activityManager; |
|
82 | + |
|
83 | + /** @var SettingsManager */ |
|
84 | + private $settingsManager; |
|
85 | + |
|
86 | + /** @var Defaults */ |
|
87 | + private $defaults; |
|
88 | + |
|
89 | + /** @var IHasher */ |
|
90 | + private $hasher; |
|
91 | + |
|
92 | + /** @var CapabilitiesManager */ |
|
93 | + private $capabilitiesManager; |
|
94 | + |
|
95 | + /** |
|
96 | + * Return the identifier of this provider. |
|
97 | + * |
|
98 | + * @return string Containing only [a-zA-Z0-9] |
|
99 | + */ |
|
100 | + public function identifier() { |
|
101 | + return 'ocMailShare'; |
|
102 | + } |
|
103 | + |
|
104 | + /** |
|
105 | + * DefaultShareProvider constructor. |
|
106 | + * |
|
107 | + * @param IDBConnection $connection |
|
108 | + * @param ISecureRandom $secureRandom |
|
109 | + * @param IUserManager $userManager |
|
110 | + * @param IRootFolder $rootFolder |
|
111 | + * @param IL10N $l |
|
112 | + * @param ILogger $logger |
|
113 | + * @param IMailer $mailer |
|
114 | + * @param IURLGenerator $urlGenerator |
|
115 | + * @param IManager $activityManager |
|
116 | + * @param SettingsManager $settingsManager |
|
117 | + * @param Defaults $defaults |
|
118 | + * @param IHasher $hasher |
|
119 | + * @param CapabilitiesManager $capabilitiesManager |
|
120 | + */ |
|
121 | + public function __construct( |
|
122 | + IDBConnection $connection, |
|
123 | + ISecureRandom $secureRandom, |
|
124 | + IUserManager $userManager, |
|
125 | + IRootFolder $rootFolder, |
|
126 | + IL10N $l, |
|
127 | + ILogger $logger, |
|
128 | + IMailer $mailer, |
|
129 | + IURLGenerator $urlGenerator, |
|
130 | + IManager $activityManager, |
|
131 | + SettingsManager $settingsManager, |
|
132 | + Defaults $defaults, |
|
133 | + IHasher $hasher, |
|
134 | + CapabilitiesManager $capabilitiesManager |
|
135 | + ) { |
|
136 | + $this->dbConnection = $connection; |
|
137 | + $this->secureRandom = $secureRandom; |
|
138 | + $this->userManager = $userManager; |
|
139 | + $this->rootFolder = $rootFolder; |
|
140 | + $this->l = $l; |
|
141 | + $this->logger = $logger; |
|
142 | + $this->mailer = $mailer; |
|
143 | + $this->urlGenerator = $urlGenerator; |
|
144 | + $this->activityManager = $activityManager; |
|
145 | + $this->settingsManager = $settingsManager; |
|
146 | + $this->defaults = $defaults; |
|
147 | + $this->hasher = $hasher; |
|
148 | + $this->capabilitiesManager = $capabilitiesManager; |
|
149 | + } |
|
150 | + |
|
151 | + /** |
|
152 | + * Share a path |
|
153 | + * |
|
154 | + * @param IShare $share |
|
155 | + * @return IShare The share object |
|
156 | + * @throws ShareNotFound |
|
157 | + * @throws \Exception |
|
158 | + */ |
|
159 | + public function create(IShare $share) { |
|
160 | + |
|
161 | + $shareWith = $share->getSharedWith(); |
|
162 | + /* |
|
163 | 163 | * Check if file is not already shared with the remote user |
164 | 164 | */ |
165 | - $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
166 | - if (!empty($alreadyShared)) { |
|
167 | - $message = 'Sharing %s failed, this item is already shared with %s'; |
|
168 | - $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
169 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
170 | - throw new \Exception($message_t); |
|
171 | - } |
|
172 | - |
|
173 | - // if the admin enforces a password for all mail shares we create a |
|
174 | - // random password and send it to the recipient |
|
175 | - $password = ''; |
|
176 | - $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
177 | - if ($passwordEnforced) { |
|
178 | - $password = $this->autoGeneratePassword($share); |
|
179 | - } |
|
180 | - |
|
181 | - $shareId = $this->createMailShare($share); |
|
182 | - $send = $this->sendPassword($share, $password); |
|
183 | - if ($passwordEnforced && $send === false) { |
|
184 | - $this->sendPasswordToOwner($share, $password); |
|
185 | - } |
|
186 | - |
|
187 | - $this->createShareActivity($share); |
|
188 | - $data = $this->getRawShare($shareId); |
|
189 | - |
|
190 | - return $this->createShareObject($data); |
|
191 | - |
|
192 | - } |
|
193 | - |
|
194 | - /** |
|
195 | - * auto generate password in case of password enforcement on mail shares |
|
196 | - * |
|
197 | - * @param IShare $share |
|
198 | - * @return string |
|
199 | - * @throws \Exception |
|
200 | - */ |
|
201 | - protected function autoGeneratePassword($share) { |
|
202 | - $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
203 | - $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
204 | - $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
205 | - |
|
206 | - if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
207 | - throw new \Exception( |
|
208 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
209 | - ); |
|
210 | - } |
|
211 | - |
|
212 | - $passwordPolicy = $this->getPasswordPolicy(); |
|
213 | - $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
214 | - $passwordLength = 8; |
|
215 | - if (!empty($passwordPolicy)) { |
|
216 | - $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
217 | - $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
218 | - } |
|
219 | - |
|
220 | - $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
221 | - |
|
222 | - $share->setPassword($this->hasher->hash($password)); |
|
223 | - |
|
224 | - return $password; |
|
225 | - } |
|
226 | - |
|
227 | - /** |
|
228 | - * get password policy |
|
229 | - * |
|
230 | - * @return array |
|
231 | - */ |
|
232 | - protected function getPasswordPolicy() { |
|
233 | - $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
234 | - if (isset($capabilities['password_policy'])) { |
|
235 | - return $capabilities['password_policy']; |
|
236 | - } |
|
237 | - |
|
238 | - return []; |
|
239 | - } |
|
240 | - |
|
241 | - /** |
|
242 | - * create activity if a file/folder was shared by mail |
|
243 | - * |
|
244 | - * @param IShare $share |
|
245 | - */ |
|
246 | - protected function createShareActivity(IShare $share) { |
|
247 | - |
|
248 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
249 | - |
|
250 | - $this->publishActivity( |
|
251 | - Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
252 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
253 | - $share->getSharedBy(), |
|
254 | - $share->getNode()->getId(), |
|
255 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
256 | - ); |
|
257 | - |
|
258 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
259 | - $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
260 | - $fileId = $share->getNode()->getId(); |
|
261 | - $nodes = $ownerFolder->getById($fileId); |
|
262 | - $ownerPath = $nodes[0]->getPath(); |
|
263 | - $this->publishActivity( |
|
264 | - Activity::SUBJECT_SHARED_EMAIL_BY, |
|
265 | - [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
266 | - $share->getShareOwner(), |
|
267 | - $fileId, |
|
268 | - $ownerFolder->getRelativePath($ownerPath) |
|
269 | - ); |
|
270 | - } |
|
271 | - |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * create activity if a file/folder was shared by mail |
|
276 | - * |
|
277 | - * @param IShare $share |
|
278 | - * @param string $sharedWith |
|
279 | - * @param bool $sendToSelf |
|
280 | - */ |
|
281 | - protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
282 | - |
|
283 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
284 | - |
|
285 | - if ($sendToSelf) { |
|
286 | - $this->publishActivity( |
|
287 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
288 | - [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
289 | - $share->getSharedBy(), |
|
290 | - $share->getNode()->getId(), |
|
291 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
292 | - ); |
|
293 | - } else { |
|
294 | - $this->publishActivity( |
|
295 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
296 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
297 | - $share->getSharedBy(), |
|
298 | - $share->getNode()->getId(), |
|
299 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
300 | - ); |
|
301 | - } |
|
302 | - } |
|
303 | - |
|
304 | - |
|
305 | - /** |
|
306 | - * publish activity if a file/folder was shared by mail |
|
307 | - * |
|
308 | - * @param $subject |
|
309 | - * @param $parameters |
|
310 | - * @param $affectedUser |
|
311 | - * @param $fileId |
|
312 | - * @param $filePath |
|
313 | - */ |
|
314 | - protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
315 | - $event = $this->activityManager->generateEvent(); |
|
316 | - $event->setApp('sharebymail') |
|
317 | - ->setType('shared') |
|
318 | - ->setSubject($subject, $parameters) |
|
319 | - ->setAffectedUser($affectedUser) |
|
320 | - ->setObject('files', $fileId, $filePath); |
|
321 | - $this->activityManager->publish($event); |
|
322 | - |
|
323 | - } |
|
324 | - |
|
325 | - /** |
|
326 | - * @param IShare $share |
|
327 | - * @return int |
|
328 | - * @throws \Exception |
|
329 | - */ |
|
330 | - protected function createMailShare(IShare $share) { |
|
331 | - $share->setToken($this->generateToken()); |
|
332 | - $shareId = $this->addShareToDB( |
|
333 | - $share->getNodeId(), |
|
334 | - $share->getNodeType(), |
|
335 | - $share->getSharedWith(), |
|
336 | - $share->getSharedBy(), |
|
337 | - $share->getShareOwner(), |
|
338 | - $share->getPermissions(), |
|
339 | - $share->getToken(), |
|
340 | - $share->getPassword() |
|
341 | - ); |
|
342 | - |
|
343 | - try { |
|
344 | - $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
345 | - ['token' => $share->getToken()]); |
|
346 | - $this->sendMailNotification( |
|
347 | - $share->getNode()->getName(), |
|
348 | - $link, |
|
349 | - $share->getSharedBy(), |
|
350 | - $share->getSharedWith(), |
|
351 | - $share->getExpirationDate() |
|
352 | - ); |
|
353 | - } catch (HintException $hintException) { |
|
354 | - $this->logger->logException($hintException, [ |
|
355 | - 'message' => 'Failed to send share by mail.', |
|
356 | - 'level' => ILogger::ERROR, |
|
357 | - 'app' => 'sharebymail', |
|
358 | - ]); |
|
359 | - $this->removeShareFromTable($shareId); |
|
360 | - throw $hintException; |
|
361 | - } catch (\Exception $e) { |
|
362 | - $this->logger->logException($e, [ |
|
363 | - 'message' => 'Failed to send share by mail.', |
|
364 | - 'level' => ILogger::ERROR, |
|
365 | - 'app' => 'sharebymail', |
|
366 | - ]); |
|
367 | - $this->removeShareFromTable($shareId); |
|
368 | - throw new HintException('Failed to send share by mail', |
|
369 | - $this->l->t('Failed to send share by email')); |
|
370 | - } |
|
371 | - |
|
372 | - return $shareId; |
|
373 | - |
|
374 | - } |
|
375 | - |
|
376 | - /** |
|
377 | - * @param string $filename |
|
378 | - * @param string $link |
|
379 | - * @param string $initiator |
|
380 | - * @param string $shareWith |
|
381 | - * @param \DateTime|null $expiration |
|
382 | - * @throws \Exception If mail couldn't be sent |
|
383 | - */ |
|
384 | - protected function sendMailNotification($filename, |
|
385 | - $link, |
|
386 | - $initiator, |
|
387 | - $shareWith, |
|
388 | - \DateTime $expiration = null) { |
|
389 | - $initiatorUser = $this->userManager->get($initiator); |
|
390 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
391 | - $message = $this->mailer->createMessage(); |
|
392 | - |
|
393 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
394 | - 'filename' => $filename, |
|
395 | - 'link' => $link, |
|
396 | - 'initiator' => $initiatorDisplayName, |
|
397 | - 'expiration' => $expiration, |
|
398 | - 'shareWith' => $shareWith, |
|
399 | - ]); |
|
400 | - |
|
401 | - $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename))); |
|
402 | - $emailTemplate->addHeader(); |
|
403 | - $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
404 | - $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
405 | - |
|
406 | - $emailTemplate->addBodyText( |
|
407 | - htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')), |
|
408 | - $text |
|
409 | - ); |
|
410 | - $emailTemplate->addBodyButton( |
|
411 | - $this->l->t('Open »%s«', [$filename]), |
|
412 | - $link |
|
413 | - ); |
|
414 | - |
|
415 | - $message->setTo([$shareWith]); |
|
416 | - |
|
417 | - // The "From" contains the sharers name |
|
418 | - $instanceName = $this->defaults->getName(); |
|
419 | - $senderName = $this->l->t( |
|
420 | - '%s via %s', |
|
421 | - [ |
|
422 | - $initiatorDisplayName, |
|
423 | - $instanceName |
|
424 | - ] |
|
425 | - ); |
|
426 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
427 | - |
|
428 | - // The "Reply-To" is set to the sharer if an mail address is configured |
|
429 | - // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
430 | - $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
431 | - if($initiatorEmail !== null) { |
|
432 | - $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
433 | - $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
434 | - } else { |
|
435 | - $emailTemplate->addFooter(); |
|
436 | - } |
|
437 | - |
|
438 | - $message->useTemplate($emailTemplate); |
|
439 | - $this->mailer->send($message); |
|
440 | - } |
|
441 | - |
|
442 | - /** |
|
443 | - * send password to recipient of a mail share |
|
444 | - * |
|
445 | - * @param IShare $share |
|
446 | - * @param string $password |
|
447 | - * @return bool |
|
448 | - */ |
|
449 | - protected function sendPassword(IShare $share, $password) { |
|
450 | - |
|
451 | - $filename = $share->getNode()->getName(); |
|
452 | - $initiator = $share->getSharedBy(); |
|
453 | - $shareWith = $share->getSharedWith(); |
|
454 | - |
|
455 | - if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
456 | - return false; |
|
457 | - } |
|
458 | - |
|
459 | - $initiatorUser = $this->userManager->get($initiator); |
|
460 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
461 | - $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
462 | - |
|
463 | - $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
464 | - $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
465 | - |
|
466 | - $message = $this->mailer->createMessage(); |
|
467 | - |
|
468 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
469 | - 'filename' => $filename, |
|
470 | - 'password' => $password, |
|
471 | - 'initiator' => $initiatorDisplayName, |
|
472 | - 'initiatorEmail' => $initiatorEmailAddress, |
|
473 | - 'shareWith' => $shareWith, |
|
474 | - ]); |
|
475 | - |
|
476 | - $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName])); |
|
477 | - $emailTemplate->addHeader(); |
|
478 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
479 | - $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
480 | - $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
481 | - |
|
482 | - // The "From" contains the sharers name |
|
483 | - $instanceName = $this->defaults->getName(); |
|
484 | - $senderName = $this->l->t( |
|
485 | - '%s via %s', |
|
486 | - [ |
|
487 | - $initiatorDisplayName, |
|
488 | - $instanceName |
|
489 | - ] |
|
490 | - ); |
|
491 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
492 | - if ($initiatorEmailAddress !== null) { |
|
493 | - $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
494 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
495 | - } else { |
|
496 | - $emailTemplate->addFooter(); |
|
497 | - } |
|
498 | - |
|
499 | - $message->setTo([$shareWith]); |
|
500 | - $message->useTemplate($emailTemplate); |
|
501 | - $this->mailer->send($message); |
|
502 | - |
|
503 | - $this->createPasswordSendActivity($share, $shareWith, false); |
|
504 | - |
|
505 | - return true; |
|
506 | - } |
|
507 | - |
|
508 | - /** |
|
509 | - * send auto generated password to the owner. This happens if the admin enforces |
|
510 | - * a password for mail shares and forbid to send the password by mail to the recipient |
|
511 | - * |
|
512 | - * @param IShare $share |
|
513 | - * @param string $password |
|
514 | - * @return bool |
|
515 | - * @throws \Exception |
|
516 | - */ |
|
517 | - protected function sendPasswordToOwner(IShare $share, $password) { |
|
518 | - |
|
519 | - $filename = $share->getNode()->getName(); |
|
520 | - $initiator = $this->userManager->get($share->getSharedBy()); |
|
521 | - $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
522 | - $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
523 | - $shareWith = $share->getSharedWith(); |
|
524 | - |
|
525 | - if ($initiatorEMailAddress === null) { |
|
526 | - throw new \Exception( |
|
527 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
528 | - ); |
|
529 | - } |
|
530 | - |
|
531 | - $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
532 | - |
|
533 | - $message = $this->mailer->createMessage(); |
|
534 | - $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
535 | - 'filename' => $filename, |
|
536 | - 'password' => $password, |
|
537 | - 'initiator' => $initiatorDisplayName, |
|
538 | - 'initiatorEmail' => $initiatorEMailAddress, |
|
539 | - 'shareWith' => $shareWith, |
|
540 | - ]); |
|
541 | - |
|
542 | - $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith])); |
|
543 | - $emailTemplate->addHeader(); |
|
544 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
545 | - $emailTemplate->addBodyText($bodyPart); |
|
546 | - $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
547 | - $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
548 | - $emailTemplate->addFooter(); |
|
549 | - |
|
550 | - if ($initiatorEMailAddress) { |
|
551 | - $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
552 | - } |
|
553 | - $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
554 | - $message->useTemplate($emailTemplate); |
|
555 | - $this->mailer->send($message); |
|
556 | - |
|
557 | - $this->createPasswordSendActivity($share, $shareWith, true); |
|
558 | - |
|
559 | - return true; |
|
560 | - } |
|
561 | - |
|
562 | - /** |
|
563 | - * generate share token |
|
564 | - * |
|
565 | - * @return string |
|
566 | - */ |
|
567 | - protected function generateToken($size = 15) { |
|
568 | - $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
569 | - return $token; |
|
570 | - } |
|
571 | - |
|
572 | - /** |
|
573 | - * Get all children of this share |
|
574 | - * |
|
575 | - * @param IShare $parent |
|
576 | - * @return IShare[] |
|
577 | - */ |
|
578 | - public function getChildren(IShare $parent) { |
|
579 | - $children = []; |
|
580 | - |
|
581 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
582 | - $qb->select('*') |
|
583 | - ->from('share') |
|
584 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
585 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
586 | - ->orderBy('id'); |
|
587 | - |
|
588 | - $cursor = $qb->execute(); |
|
589 | - while($data = $cursor->fetch()) { |
|
590 | - $children[] = $this->createShareObject($data); |
|
591 | - } |
|
592 | - $cursor->closeCursor(); |
|
593 | - |
|
594 | - return $children; |
|
595 | - } |
|
596 | - |
|
597 | - /** |
|
598 | - * add share to the database and return the ID |
|
599 | - * |
|
600 | - * @param int $itemSource |
|
601 | - * @param string $itemType |
|
602 | - * @param string $shareWith |
|
603 | - * @param string $sharedBy |
|
604 | - * @param string $uidOwner |
|
605 | - * @param int $permissions |
|
606 | - * @param string $token |
|
607 | - * @return int |
|
608 | - */ |
|
609 | - protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
610 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
611 | - $qb->insert('share') |
|
612 | - ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
613 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
614 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
615 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
616 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
617 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
618 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
619 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
620 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
621 | - ->setValue('password', $qb->createNamedParameter($password)) |
|
622 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
623 | - |
|
624 | - /* |
|
165 | + $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
166 | + if (!empty($alreadyShared)) { |
|
167 | + $message = 'Sharing %s failed, this item is already shared with %s'; |
|
168 | + $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
169 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
170 | + throw new \Exception($message_t); |
|
171 | + } |
|
172 | + |
|
173 | + // if the admin enforces a password for all mail shares we create a |
|
174 | + // random password and send it to the recipient |
|
175 | + $password = ''; |
|
176 | + $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
177 | + if ($passwordEnforced) { |
|
178 | + $password = $this->autoGeneratePassword($share); |
|
179 | + } |
|
180 | + |
|
181 | + $shareId = $this->createMailShare($share); |
|
182 | + $send = $this->sendPassword($share, $password); |
|
183 | + if ($passwordEnforced && $send === false) { |
|
184 | + $this->sendPasswordToOwner($share, $password); |
|
185 | + } |
|
186 | + |
|
187 | + $this->createShareActivity($share); |
|
188 | + $data = $this->getRawShare($shareId); |
|
189 | + |
|
190 | + return $this->createShareObject($data); |
|
191 | + |
|
192 | + } |
|
193 | + |
|
194 | + /** |
|
195 | + * auto generate password in case of password enforcement on mail shares |
|
196 | + * |
|
197 | + * @param IShare $share |
|
198 | + * @return string |
|
199 | + * @throws \Exception |
|
200 | + */ |
|
201 | + protected function autoGeneratePassword($share) { |
|
202 | + $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
203 | + $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
204 | + $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
205 | + |
|
206 | + if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
207 | + throw new \Exception( |
|
208 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
209 | + ); |
|
210 | + } |
|
211 | + |
|
212 | + $passwordPolicy = $this->getPasswordPolicy(); |
|
213 | + $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
214 | + $passwordLength = 8; |
|
215 | + if (!empty($passwordPolicy)) { |
|
216 | + $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
217 | + $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
218 | + } |
|
219 | + |
|
220 | + $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
221 | + |
|
222 | + $share->setPassword($this->hasher->hash($password)); |
|
223 | + |
|
224 | + return $password; |
|
225 | + } |
|
226 | + |
|
227 | + /** |
|
228 | + * get password policy |
|
229 | + * |
|
230 | + * @return array |
|
231 | + */ |
|
232 | + protected function getPasswordPolicy() { |
|
233 | + $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
234 | + if (isset($capabilities['password_policy'])) { |
|
235 | + return $capabilities['password_policy']; |
|
236 | + } |
|
237 | + |
|
238 | + return []; |
|
239 | + } |
|
240 | + |
|
241 | + /** |
|
242 | + * create activity if a file/folder was shared by mail |
|
243 | + * |
|
244 | + * @param IShare $share |
|
245 | + */ |
|
246 | + protected function createShareActivity(IShare $share) { |
|
247 | + |
|
248 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
249 | + |
|
250 | + $this->publishActivity( |
|
251 | + Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
252 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
253 | + $share->getSharedBy(), |
|
254 | + $share->getNode()->getId(), |
|
255 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
256 | + ); |
|
257 | + |
|
258 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
259 | + $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
260 | + $fileId = $share->getNode()->getId(); |
|
261 | + $nodes = $ownerFolder->getById($fileId); |
|
262 | + $ownerPath = $nodes[0]->getPath(); |
|
263 | + $this->publishActivity( |
|
264 | + Activity::SUBJECT_SHARED_EMAIL_BY, |
|
265 | + [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
266 | + $share->getShareOwner(), |
|
267 | + $fileId, |
|
268 | + $ownerFolder->getRelativePath($ownerPath) |
|
269 | + ); |
|
270 | + } |
|
271 | + |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * create activity if a file/folder was shared by mail |
|
276 | + * |
|
277 | + * @param IShare $share |
|
278 | + * @param string $sharedWith |
|
279 | + * @param bool $sendToSelf |
|
280 | + */ |
|
281 | + protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
282 | + |
|
283 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
284 | + |
|
285 | + if ($sendToSelf) { |
|
286 | + $this->publishActivity( |
|
287 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
288 | + [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
289 | + $share->getSharedBy(), |
|
290 | + $share->getNode()->getId(), |
|
291 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
292 | + ); |
|
293 | + } else { |
|
294 | + $this->publishActivity( |
|
295 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
296 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
297 | + $share->getSharedBy(), |
|
298 | + $share->getNode()->getId(), |
|
299 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
300 | + ); |
|
301 | + } |
|
302 | + } |
|
303 | + |
|
304 | + |
|
305 | + /** |
|
306 | + * publish activity if a file/folder was shared by mail |
|
307 | + * |
|
308 | + * @param $subject |
|
309 | + * @param $parameters |
|
310 | + * @param $affectedUser |
|
311 | + * @param $fileId |
|
312 | + * @param $filePath |
|
313 | + */ |
|
314 | + protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
315 | + $event = $this->activityManager->generateEvent(); |
|
316 | + $event->setApp('sharebymail') |
|
317 | + ->setType('shared') |
|
318 | + ->setSubject($subject, $parameters) |
|
319 | + ->setAffectedUser($affectedUser) |
|
320 | + ->setObject('files', $fileId, $filePath); |
|
321 | + $this->activityManager->publish($event); |
|
322 | + |
|
323 | + } |
|
324 | + |
|
325 | + /** |
|
326 | + * @param IShare $share |
|
327 | + * @return int |
|
328 | + * @throws \Exception |
|
329 | + */ |
|
330 | + protected function createMailShare(IShare $share) { |
|
331 | + $share->setToken($this->generateToken()); |
|
332 | + $shareId = $this->addShareToDB( |
|
333 | + $share->getNodeId(), |
|
334 | + $share->getNodeType(), |
|
335 | + $share->getSharedWith(), |
|
336 | + $share->getSharedBy(), |
|
337 | + $share->getShareOwner(), |
|
338 | + $share->getPermissions(), |
|
339 | + $share->getToken(), |
|
340 | + $share->getPassword() |
|
341 | + ); |
|
342 | + |
|
343 | + try { |
|
344 | + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
345 | + ['token' => $share->getToken()]); |
|
346 | + $this->sendMailNotification( |
|
347 | + $share->getNode()->getName(), |
|
348 | + $link, |
|
349 | + $share->getSharedBy(), |
|
350 | + $share->getSharedWith(), |
|
351 | + $share->getExpirationDate() |
|
352 | + ); |
|
353 | + } catch (HintException $hintException) { |
|
354 | + $this->logger->logException($hintException, [ |
|
355 | + 'message' => 'Failed to send share by mail.', |
|
356 | + 'level' => ILogger::ERROR, |
|
357 | + 'app' => 'sharebymail', |
|
358 | + ]); |
|
359 | + $this->removeShareFromTable($shareId); |
|
360 | + throw $hintException; |
|
361 | + } catch (\Exception $e) { |
|
362 | + $this->logger->logException($e, [ |
|
363 | + 'message' => 'Failed to send share by mail.', |
|
364 | + 'level' => ILogger::ERROR, |
|
365 | + 'app' => 'sharebymail', |
|
366 | + ]); |
|
367 | + $this->removeShareFromTable($shareId); |
|
368 | + throw new HintException('Failed to send share by mail', |
|
369 | + $this->l->t('Failed to send share by email')); |
|
370 | + } |
|
371 | + |
|
372 | + return $shareId; |
|
373 | + |
|
374 | + } |
|
375 | + |
|
376 | + /** |
|
377 | + * @param string $filename |
|
378 | + * @param string $link |
|
379 | + * @param string $initiator |
|
380 | + * @param string $shareWith |
|
381 | + * @param \DateTime|null $expiration |
|
382 | + * @throws \Exception If mail couldn't be sent |
|
383 | + */ |
|
384 | + protected function sendMailNotification($filename, |
|
385 | + $link, |
|
386 | + $initiator, |
|
387 | + $shareWith, |
|
388 | + \DateTime $expiration = null) { |
|
389 | + $initiatorUser = $this->userManager->get($initiator); |
|
390 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
391 | + $message = $this->mailer->createMessage(); |
|
392 | + |
|
393 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [ |
|
394 | + 'filename' => $filename, |
|
395 | + 'link' => $link, |
|
396 | + 'initiator' => $initiatorDisplayName, |
|
397 | + 'expiration' => $expiration, |
|
398 | + 'shareWith' => $shareWith, |
|
399 | + ]); |
|
400 | + |
|
401 | + $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename))); |
|
402 | + $emailTemplate->addHeader(); |
|
403 | + $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
404 | + $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
405 | + |
|
406 | + $emailTemplate->addBodyText( |
|
407 | + htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')), |
|
408 | + $text |
|
409 | + ); |
|
410 | + $emailTemplate->addBodyButton( |
|
411 | + $this->l->t('Open »%s«', [$filename]), |
|
412 | + $link |
|
413 | + ); |
|
414 | + |
|
415 | + $message->setTo([$shareWith]); |
|
416 | + |
|
417 | + // The "From" contains the sharers name |
|
418 | + $instanceName = $this->defaults->getName(); |
|
419 | + $senderName = $this->l->t( |
|
420 | + '%s via %s', |
|
421 | + [ |
|
422 | + $initiatorDisplayName, |
|
423 | + $instanceName |
|
424 | + ] |
|
425 | + ); |
|
426 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
427 | + |
|
428 | + // The "Reply-To" is set to the sharer if an mail address is configured |
|
429 | + // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
430 | + $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
431 | + if($initiatorEmail !== null) { |
|
432 | + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
433 | + $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); |
|
434 | + } else { |
|
435 | + $emailTemplate->addFooter(); |
|
436 | + } |
|
437 | + |
|
438 | + $message->useTemplate($emailTemplate); |
|
439 | + $this->mailer->send($message); |
|
440 | + } |
|
441 | + |
|
442 | + /** |
|
443 | + * send password to recipient of a mail share |
|
444 | + * |
|
445 | + * @param IShare $share |
|
446 | + * @param string $password |
|
447 | + * @return bool |
|
448 | + */ |
|
449 | + protected function sendPassword(IShare $share, $password) { |
|
450 | + |
|
451 | + $filename = $share->getNode()->getName(); |
|
452 | + $initiator = $share->getSharedBy(); |
|
453 | + $shareWith = $share->getSharedWith(); |
|
454 | + |
|
455 | + if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
456 | + return false; |
|
457 | + } |
|
458 | + |
|
459 | + $initiatorUser = $this->userManager->get($initiator); |
|
460 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
461 | + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
462 | + |
|
463 | + $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
464 | + $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
465 | + |
|
466 | + $message = $this->mailer->createMessage(); |
|
467 | + |
|
468 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [ |
|
469 | + 'filename' => $filename, |
|
470 | + 'password' => $password, |
|
471 | + 'initiator' => $initiatorDisplayName, |
|
472 | + 'initiatorEmail' => $initiatorEmailAddress, |
|
473 | + 'shareWith' => $shareWith, |
|
474 | + ]); |
|
475 | + |
|
476 | + $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName])); |
|
477 | + $emailTemplate->addHeader(); |
|
478 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
479 | + $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart); |
|
480 | + $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
481 | + |
|
482 | + // The "From" contains the sharers name |
|
483 | + $instanceName = $this->defaults->getName(); |
|
484 | + $senderName = $this->l->t( |
|
485 | + '%s via %s', |
|
486 | + [ |
|
487 | + $initiatorDisplayName, |
|
488 | + $instanceName |
|
489 | + ] |
|
490 | + ); |
|
491 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
492 | + if ($initiatorEmailAddress !== null) { |
|
493 | + $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
494 | + $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
495 | + } else { |
|
496 | + $emailTemplate->addFooter(); |
|
497 | + } |
|
498 | + |
|
499 | + $message->setTo([$shareWith]); |
|
500 | + $message->useTemplate($emailTemplate); |
|
501 | + $this->mailer->send($message); |
|
502 | + |
|
503 | + $this->createPasswordSendActivity($share, $shareWith, false); |
|
504 | + |
|
505 | + return true; |
|
506 | + } |
|
507 | + |
|
508 | + /** |
|
509 | + * send auto generated password to the owner. This happens if the admin enforces |
|
510 | + * a password for mail shares and forbid to send the password by mail to the recipient |
|
511 | + * |
|
512 | + * @param IShare $share |
|
513 | + * @param string $password |
|
514 | + * @return bool |
|
515 | + * @throws \Exception |
|
516 | + */ |
|
517 | + protected function sendPasswordToOwner(IShare $share, $password) { |
|
518 | + |
|
519 | + $filename = $share->getNode()->getName(); |
|
520 | + $initiator = $this->userManager->get($share->getSharedBy()); |
|
521 | + $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
522 | + $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
523 | + $shareWith = $share->getSharedWith(); |
|
524 | + |
|
525 | + if ($initiatorEMailAddress === null) { |
|
526 | + throw new \Exception( |
|
527 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
528 | + ); |
|
529 | + } |
|
530 | + |
|
531 | + $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
532 | + |
|
533 | + $message = $this->mailer->createMessage(); |
|
534 | + $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [ |
|
535 | + 'filename' => $filename, |
|
536 | + 'password' => $password, |
|
537 | + 'initiator' => $initiatorDisplayName, |
|
538 | + 'initiatorEmail' => $initiatorEMailAddress, |
|
539 | + 'shareWith' => $shareWith, |
|
540 | + ]); |
|
541 | + |
|
542 | + $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith])); |
|
543 | + $emailTemplate->addHeader(); |
|
544 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
545 | + $emailTemplate->addBodyText($bodyPart); |
|
546 | + $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
547 | + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
548 | + $emailTemplate->addFooter(); |
|
549 | + |
|
550 | + if ($initiatorEMailAddress) { |
|
551 | + $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
552 | + } |
|
553 | + $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
554 | + $message->useTemplate($emailTemplate); |
|
555 | + $this->mailer->send($message); |
|
556 | + |
|
557 | + $this->createPasswordSendActivity($share, $shareWith, true); |
|
558 | + |
|
559 | + return true; |
|
560 | + } |
|
561 | + |
|
562 | + /** |
|
563 | + * generate share token |
|
564 | + * |
|
565 | + * @return string |
|
566 | + */ |
|
567 | + protected function generateToken($size = 15) { |
|
568 | + $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE); |
|
569 | + return $token; |
|
570 | + } |
|
571 | + |
|
572 | + /** |
|
573 | + * Get all children of this share |
|
574 | + * |
|
575 | + * @param IShare $parent |
|
576 | + * @return IShare[] |
|
577 | + */ |
|
578 | + public function getChildren(IShare $parent) { |
|
579 | + $children = []; |
|
580 | + |
|
581 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
582 | + $qb->select('*') |
|
583 | + ->from('share') |
|
584 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
585 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
586 | + ->orderBy('id'); |
|
587 | + |
|
588 | + $cursor = $qb->execute(); |
|
589 | + while($data = $cursor->fetch()) { |
|
590 | + $children[] = $this->createShareObject($data); |
|
591 | + } |
|
592 | + $cursor->closeCursor(); |
|
593 | + |
|
594 | + return $children; |
|
595 | + } |
|
596 | + |
|
597 | + /** |
|
598 | + * add share to the database and return the ID |
|
599 | + * |
|
600 | + * @param int $itemSource |
|
601 | + * @param string $itemType |
|
602 | + * @param string $shareWith |
|
603 | + * @param string $sharedBy |
|
604 | + * @param string $uidOwner |
|
605 | + * @param int $permissions |
|
606 | + * @param string $token |
|
607 | + * @return int |
|
608 | + */ |
|
609 | + protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
610 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
611 | + $qb->insert('share') |
|
612 | + ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
613 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
614 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
615 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
616 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
617 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
618 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
619 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
620 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
621 | + ->setValue('password', $qb->createNamedParameter($password)) |
|
622 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
623 | + |
|
624 | + /* |
|
625 | 625 | * Added to fix https://github.com/owncloud/core/issues/22215 |
626 | 626 | * Can be removed once we get rid of ajax/share.php |
627 | 627 | */ |
628 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
628 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
629 | 629 | |
630 | - $qb->execute(); |
|
631 | - $id = $qb->getLastInsertId(); |
|
630 | + $qb->execute(); |
|
631 | + $id = $qb->getLastInsertId(); |
|
632 | 632 | |
633 | - return (int)$id; |
|
634 | - } |
|
633 | + return (int)$id; |
|
634 | + } |
|
635 | 635 | |
636 | - /** |
|
637 | - * Update a share |
|
638 | - * |
|
639 | - * @param IShare $share |
|
640 | - * @param string|null $plainTextPassword |
|
641 | - * @return IShare The share object |
|
642 | - */ |
|
643 | - public function update(IShare $share, $plainTextPassword = null) { |
|
636 | + /** |
|
637 | + * Update a share |
|
638 | + * |
|
639 | + * @param IShare $share |
|
640 | + * @param string|null $plainTextPassword |
|
641 | + * @return IShare The share object |
|
642 | + */ |
|
643 | + public function update(IShare $share, $plainTextPassword = null) { |
|
644 | 644 | |
645 | - $originalShare = $this->getShareById($share->getId()); |
|
645 | + $originalShare = $this->getShareById($share->getId()); |
|
646 | 646 | |
647 | - // a real password was given |
|
648 | - $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
647 | + // a real password was given |
|
648 | + $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
649 | 649 | |
650 | - if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
651 | - $this->sendPassword($share, $plainTextPassword); |
|
652 | - } |
|
653 | - /* |
|
650 | + if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
651 | + $this->sendPassword($share, $plainTextPassword); |
|
652 | + } |
|
653 | + /* |
|
654 | 654 | * We allow updating the permissions and password of mail shares |
655 | 655 | */ |
656 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
657 | - $qb->update('share') |
|
658 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
659 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
660 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
661 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
662 | - ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
663 | - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
664 | - ->execute(); |
|
665 | - |
|
666 | - return $share; |
|
667 | - } |
|
668 | - |
|
669 | - /** |
|
670 | - * @inheritdoc |
|
671 | - */ |
|
672 | - public function move(IShare $share, $recipient) { |
|
673 | - /** |
|
674 | - * nothing to do here, mail shares are only outgoing shares |
|
675 | - */ |
|
676 | - return $share; |
|
677 | - } |
|
678 | - |
|
679 | - /** |
|
680 | - * Delete a share (owner unShares the file) |
|
681 | - * |
|
682 | - * @param IShare $share |
|
683 | - */ |
|
684 | - public function delete(IShare $share) { |
|
685 | - $this->removeShareFromTable($share->getId()); |
|
686 | - } |
|
687 | - |
|
688 | - /** |
|
689 | - * @inheritdoc |
|
690 | - */ |
|
691 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
692 | - // nothing to do here, mail shares are only outgoing shares |
|
693 | - } |
|
694 | - |
|
695 | - /** |
|
696 | - * @inheritdoc |
|
697 | - */ |
|
698 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
699 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | - $qb->select('*') |
|
701 | - ->from('share'); |
|
702 | - |
|
703 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
704 | - |
|
705 | - /** |
|
706 | - * Reshares for this user are shares where they are the owner. |
|
707 | - */ |
|
708 | - if ($reshares === false) { |
|
709 | - //Special case for old shares created via the web UI |
|
710 | - $or1 = $qb->expr()->andX( |
|
711 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
712 | - $qb->expr()->isNull('uid_initiator') |
|
713 | - ); |
|
714 | - |
|
715 | - $qb->andWhere( |
|
716 | - $qb->expr()->orX( |
|
717 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
718 | - $or1 |
|
719 | - ) |
|
720 | - ); |
|
721 | - } else { |
|
722 | - $qb->andWhere( |
|
723 | - $qb->expr()->orX( |
|
724 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
725 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
726 | - ) |
|
727 | - ); |
|
728 | - } |
|
729 | - |
|
730 | - if ($node !== null) { |
|
731 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
732 | - } |
|
733 | - |
|
734 | - if ($limit !== -1) { |
|
735 | - $qb->setMaxResults($limit); |
|
736 | - } |
|
737 | - |
|
738 | - $qb->setFirstResult($offset); |
|
739 | - $qb->orderBy('id'); |
|
740 | - |
|
741 | - $cursor = $qb->execute(); |
|
742 | - $shares = []; |
|
743 | - while($data = $cursor->fetch()) { |
|
744 | - $shares[] = $this->createShareObject($data); |
|
745 | - } |
|
746 | - $cursor->closeCursor(); |
|
747 | - |
|
748 | - return $shares; |
|
749 | - } |
|
750 | - |
|
751 | - /** |
|
752 | - * @inheritdoc |
|
753 | - */ |
|
754 | - public function getShareById($id, $recipientId = null) { |
|
755 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | - |
|
757 | - $qb->select('*') |
|
758 | - ->from('share') |
|
759 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
760 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
761 | - |
|
762 | - $cursor = $qb->execute(); |
|
763 | - $data = $cursor->fetch(); |
|
764 | - $cursor->closeCursor(); |
|
765 | - |
|
766 | - if ($data === false) { |
|
767 | - throw new ShareNotFound(); |
|
768 | - } |
|
769 | - |
|
770 | - try { |
|
771 | - $share = $this->createShareObject($data); |
|
772 | - } catch (InvalidShare $e) { |
|
773 | - throw new ShareNotFound(); |
|
774 | - } |
|
775 | - |
|
776 | - return $share; |
|
777 | - } |
|
778 | - |
|
779 | - /** |
|
780 | - * Get shares for a given path |
|
781 | - * |
|
782 | - * @param \OCP\Files\Node $path |
|
783 | - * @return IShare[] |
|
784 | - */ |
|
785 | - public function getSharesByPath(Node $path) { |
|
786 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
787 | - |
|
788 | - $cursor = $qb->select('*') |
|
789 | - ->from('share') |
|
790 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
791 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
792 | - ->execute(); |
|
793 | - |
|
794 | - $shares = []; |
|
795 | - while($data = $cursor->fetch()) { |
|
796 | - $shares[] = $this->createShareObject($data); |
|
797 | - } |
|
798 | - $cursor->closeCursor(); |
|
799 | - |
|
800 | - return $shares; |
|
801 | - } |
|
802 | - |
|
803 | - /** |
|
804 | - * @inheritdoc |
|
805 | - */ |
|
806 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
807 | - /** @var IShare[] $shares */ |
|
808 | - $shares = []; |
|
809 | - |
|
810 | - //Get shares directly with this user |
|
811 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
812 | - $qb->select('*') |
|
813 | - ->from('share'); |
|
814 | - |
|
815 | - // Order by id |
|
816 | - $qb->orderBy('id'); |
|
817 | - |
|
818 | - // Set limit and offset |
|
819 | - if ($limit !== -1) { |
|
820 | - $qb->setMaxResults($limit); |
|
821 | - } |
|
822 | - $qb->setFirstResult($offset); |
|
823 | - |
|
824 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
825 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
826 | - |
|
827 | - // Filter by node if provided |
|
828 | - if ($node !== null) { |
|
829 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
830 | - } |
|
831 | - |
|
832 | - $cursor = $qb->execute(); |
|
833 | - |
|
834 | - while($data = $cursor->fetch()) { |
|
835 | - $shares[] = $this->createShareObject($data); |
|
836 | - } |
|
837 | - $cursor->closeCursor(); |
|
838 | - |
|
839 | - |
|
840 | - return $shares; |
|
841 | - } |
|
842 | - |
|
843 | - /** |
|
844 | - * Get a share by token |
|
845 | - * |
|
846 | - * @param string $token |
|
847 | - * @return IShare |
|
848 | - * @throws ShareNotFound |
|
849 | - */ |
|
850 | - public function getShareByToken($token) { |
|
851 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
852 | - |
|
853 | - $cursor = $qb->select('*') |
|
854 | - ->from('share') |
|
855 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
856 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
857 | - ->execute(); |
|
858 | - |
|
859 | - $data = $cursor->fetch(); |
|
860 | - |
|
861 | - if ($data === false) { |
|
862 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
863 | - } |
|
864 | - |
|
865 | - try { |
|
866 | - $share = $this->createShareObject($data); |
|
867 | - } catch (InvalidShare $e) { |
|
868 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
869 | - } |
|
870 | - |
|
871 | - return $share; |
|
872 | - } |
|
873 | - |
|
874 | - /** |
|
875 | - * remove share from table |
|
876 | - * |
|
877 | - * @param string $shareId |
|
878 | - */ |
|
879 | - protected function removeShareFromTable($shareId) { |
|
880 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
881 | - $qb->delete('share') |
|
882 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
883 | - $qb->execute(); |
|
884 | - } |
|
885 | - |
|
886 | - /** |
|
887 | - * Create a share object from an database row |
|
888 | - * |
|
889 | - * @param array $data |
|
890 | - * @return IShare |
|
891 | - * @throws InvalidShare |
|
892 | - * @throws ShareNotFound |
|
893 | - */ |
|
894 | - protected function createShareObject($data) { |
|
895 | - |
|
896 | - $share = new Share($this->rootFolder, $this->userManager); |
|
897 | - $share->setId((int)$data['id']) |
|
898 | - ->setShareType((int)$data['share_type']) |
|
899 | - ->setPermissions((int)$data['permissions']) |
|
900 | - ->setTarget($data['file_target']) |
|
901 | - ->setMailSend((bool)$data['mail_send']) |
|
902 | - ->setToken($data['token']); |
|
903 | - |
|
904 | - $shareTime = new \DateTime(); |
|
905 | - $shareTime->setTimestamp((int)$data['stime']); |
|
906 | - $share->setShareTime($shareTime); |
|
907 | - $share->setSharedWith($data['share_with']); |
|
908 | - $share->setPassword($data['password']); |
|
909 | - |
|
910 | - if ($data['uid_initiator'] !== null) { |
|
911 | - $share->setShareOwner($data['uid_owner']); |
|
912 | - $share->setSharedBy($data['uid_initiator']); |
|
913 | - } else { |
|
914 | - //OLD SHARE |
|
915 | - $share->setSharedBy($data['uid_owner']); |
|
916 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
917 | - |
|
918 | - $owner = $path->getOwner(); |
|
919 | - $share->setShareOwner($owner->getUID()); |
|
920 | - } |
|
921 | - |
|
922 | - if ($data['expiration'] !== null) { |
|
923 | - $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
924 | - if ($expiration !== false) { |
|
925 | - $share->setExpirationDate($expiration); |
|
926 | - } |
|
927 | - } |
|
928 | - |
|
929 | - $share->setNodeId((int)$data['file_source']); |
|
930 | - $share->setNodeType($data['item_type']); |
|
931 | - |
|
932 | - $share->setProviderId($this->identifier()); |
|
933 | - |
|
934 | - return $share; |
|
935 | - } |
|
936 | - |
|
937 | - /** |
|
938 | - * Get the node with file $id for $user |
|
939 | - * |
|
940 | - * @param string $userId |
|
941 | - * @param int $id |
|
942 | - * @return \OCP\Files\File|\OCP\Files\Folder |
|
943 | - * @throws InvalidShare |
|
944 | - */ |
|
945 | - private function getNode($userId, $id) { |
|
946 | - try { |
|
947 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
948 | - } catch (NoUserException $e) { |
|
949 | - throw new InvalidShare(); |
|
950 | - } |
|
951 | - |
|
952 | - $nodes = $userFolder->getById($id); |
|
953 | - |
|
954 | - if (empty($nodes)) { |
|
955 | - throw new InvalidShare(); |
|
956 | - } |
|
957 | - |
|
958 | - return $nodes[0]; |
|
959 | - } |
|
960 | - |
|
961 | - /** |
|
962 | - * A user is deleted from the system |
|
963 | - * So clean up the relevant shares. |
|
964 | - * |
|
965 | - * @param string $uid |
|
966 | - * @param int $shareType |
|
967 | - */ |
|
968 | - public function userDeleted($uid, $shareType) { |
|
969 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
970 | - |
|
971 | - $qb->delete('share') |
|
972 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
973 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
974 | - ->execute(); |
|
975 | - } |
|
976 | - |
|
977 | - /** |
|
978 | - * This provider does not support group shares |
|
979 | - * |
|
980 | - * @param string $gid |
|
981 | - */ |
|
982 | - public function groupDeleted($gid) { |
|
983 | - } |
|
984 | - |
|
985 | - /** |
|
986 | - * This provider does not support group shares |
|
987 | - * |
|
988 | - * @param string $uid |
|
989 | - * @param string $gid |
|
990 | - */ |
|
991 | - public function userDeletedFromGroup($uid, $gid) { |
|
992 | - } |
|
993 | - |
|
994 | - /** |
|
995 | - * get database row of a give share |
|
996 | - * |
|
997 | - * @param $id |
|
998 | - * @return array |
|
999 | - * @throws ShareNotFound |
|
1000 | - */ |
|
1001 | - protected function getRawShare($id) { |
|
1002 | - |
|
1003 | - // Now fetch the inserted share and create a complete share object |
|
1004 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1005 | - $qb->select('*') |
|
1006 | - ->from('share') |
|
1007 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
1008 | - |
|
1009 | - $cursor = $qb->execute(); |
|
1010 | - $data = $cursor->fetch(); |
|
1011 | - $cursor->closeCursor(); |
|
1012 | - |
|
1013 | - if ($data === false) { |
|
1014 | - throw new ShareNotFound; |
|
1015 | - } |
|
1016 | - |
|
1017 | - return $data; |
|
1018 | - } |
|
1019 | - |
|
1020 | - public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
1021 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1022 | - $qb->select('*') |
|
1023 | - ->from('share', 's') |
|
1024 | - ->andWhere($qb->expr()->orX( |
|
1025 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1026 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1027 | - )) |
|
1028 | - ->andWhere( |
|
1029 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
1030 | - ); |
|
1031 | - |
|
1032 | - /** |
|
1033 | - * Reshares for this user are shares where they are the owner. |
|
1034 | - */ |
|
1035 | - if ($reshares === false) { |
|
1036 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
1037 | - } else { |
|
1038 | - $qb->andWhere( |
|
1039 | - $qb->expr()->orX( |
|
1040 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
1041 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
1042 | - ) |
|
1043 | - ); |
|
1044 | - } |
|
1045 | - |
|
1046 | - $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
1047 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
1048 | - |
|
1049 | - $qb->orderBy('id'); |
|
1050 | - |
|
1051 | - $cursor = $qb->execute(); |
|
1052 | - $shares = []; |
|
1053 | - while ($data = $cursor->fetch()) { |
|
1054 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
1055 | - } |
|
1056 | - $cursor->closeCursor(); |
|
1057 | - |
|
1058 | - return $shares; |
|
1059 | - } |
|
1060 | - |
|
1061 | - /** |
|
1062 | - * @inheritdoc |
|
1063 | - */ |
|
1064 | - public function getAccessList($nodes, $currentAccess) { |
|
1065 | - $ids = []; |
|
1066 | - foreach ($nodes as $node) { |
|
1067 | - $ids[] = $node->getId(); |
|
1068 | - } |
|
1069 | - |
|
1070 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
1071 | - $qb->select('share_with') |
|
1072 | - ->from('share') |
|
1073 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
1074 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1075 | - ->andWhere($qb->expr()->orX( |
|
1076 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1077 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1078 | - )) |
|
1079 | - ->setMaxResults(1); |
|
1080 | - $cursor = $qb->execute(); |
|
1081 | - |
|
1082 | - $mail = $cursor->fetch() !== false; |
|
1083 | - $cursor->closeCursor(); |
|
1084 | - |
|
1085 | - return ['public' => $mail]; |
|
1086 | - } |
|
656 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
657 | + $qb->update('share') |
|
658 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
659 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
660 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
661 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
662 | + ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
663 | + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
664 | + ->execute(); |
|
665 | + |
|
666 | + return $share; |
|
667 | + } |
|
668 | + |
|
669 | + /** |
|
670 | + * @inheritdoc |
|
671 | + */ |
|
672 | + public function move(IShare $share, $recipient) { |
|
673 | + /** |
|
674 | + * nothing to do here, mail shares are only outgoing shares |
|
675 | + */ |
|
676 | + return $share; |
|
677 | + } |
|
678 | + |
|
679 | + /** |
|
680 | + * Delete a share (owner unShares the file) |
|
681 | + * |
|
682 | + * @param IShare $share |
|
683 | + */ |
|
684 | + public function delete(IShare $share) { |
|
685 | + $this->removeShareFromTable($share->getId()); |
|
686 | + } |
|
687 | + |
|
688 | + /** |
|
689 | + * @inheritdoc |
|
690 | + */ |
|
691 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
692 | + // nothing to do here, mail shares are only outgoing shares |
|
693 | + } |
|
694 | + |
|
695 | + /** |
|
696 | + * @inheritdoc |
|
697 | + */ |
|
698 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
699 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
700 | + $qb->select('*') |
|
701 | + ->from('share'); |
|
702 | + |
|
703 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
704 | + |
|
705 | + /** |
|
706 | + * Reshares for this user are shares where they are the owner. |
|
707 | + */ |
|
708 | + if ($reshares === false) { |
|
709 | + //Special case for old shares created via the web UI |
|
710 | + $or1 = $qb->expr()->andX( |
|
711 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
712 | + $qb->expr()->isNull('uid_initiator') |
|
713 | + ); |
|
714 | + |
|
715 | + $qb->andWhere( |
|
716 | + $qb->expr()->orX( |
|
717 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
718 | + $or1 |
|
719 | + ) |
|
720 | + ); |
|
721 | + } else { |
|
722 | + $qb->andWhere( |
|
723 | + $qb->expr()->orX( |
|
724 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
725 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
726 | + ) |
|
727 | + ); |
|
728 | + } |
|
729 | + |
|
730 | + if ($node !== null) { |
|
731 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
732 | + } |
|
733 | + |
|
734 | + if ($limit !== -1) { |
|
735 | + $qb->setMaxResults($limit); |
|
736 | + } |
|
737 | + |
|
738 | + $qb->setFirstResult($offset); |
|
739 | + $qb->orderBy('id'); |
|
740 | + |
|
741 | + $cursor = $qb->execute(); |
|
742 | + $shares = []; |
|
743 | + while($data = $cursor->fetch()) { |
|
744 | + $shares[] = $this->createShareObject($data); |
|
745 | + } |
|
746 | + $cursor->closeCursor(); |
|
747 | + |
|
748 | + return $shares; |
|
749 | + } |
|
750 | + |
|
751 | + /** |
|
752 | + * @inheritdoc |
|
753 | + */ |
|
754 | + public function getShareById($id, $recipientId = null) { |
|
755 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
756 | + |
|
757 | + $qb->select('*') |
|
758 | + ->from('share') |
|
759 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
760 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
761 | + |
|
762 | + $cursor = $qb->execute(); |
|
763 | + $data = $cursor->fetch(); |
|
764 | + $cursor->closeCursor(); |
|
765 | + |
|
766 | + if ($data === false) { |
|
767 | + throw new ShareNotFound(); |
|
768 | + } |
|
769 | + |
|
770 | + try { |
|
771 | + $share = $this->createShareObject($data); |
|
772 | + } catch (InvalidShare $e) { |
|
773 | + throw new ShareNotFound(); |
|
774 | + } |
|
775 | + |
|
776 | + return $share; |
|
777 | + } |
|
778 | + |
|
779 | + /** |
|
780 | + * Get shares for a given path |
|
781 | + * |
|
782 | + * @param \OCP\Files\Node $path |
|
783 | + * @return IShare[] |
|
784 | + */ |
|
785 | + public function getSharesByPath(Node $path) { |
|
786 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
787 | + |
|
788 | + $cursor = $qb->select('*') |
|
789 | + ->from('share') |
|
790 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
791 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
792 | + ->execute(); |
|
793 | + |
|
794 | + $shares = []; |
|
795 | + while($data = $cursor->fetch()) { |
|
796 | + $shares[] = $this->createShareObject($data); |
|
797 | + } |
|
798 | + $cursor->closeCursor(); |
|
799 | + |
|
800 | + return $shares; |
|
801 | + } |
|
802 | + |
|
803 | + /** |
|
804 | + * @inheritdoc |
|
805 | + */ |
|
806 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
807 | + /** @var IShare[] $shares */ |
|
808 | + $shares = []; |
|
809 | + |
|
810 | + //Get shares directly with this user |
|
811 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
812 | + $qb->select('*') |
|
813 | + ->from('share'); |
|
814 | + |
|
815 | + // Order by id |
|
816 | + $qb->orderBy('id'); |
|
817 | + |
|
818 | + // Set limit and offset |
|
819 | + if ($limit !== -1) { |
|
820 | + $qb->setMaxResults($limit); |
|
821 | + } |
|
822 | + $qb->setFirstResult($offset); |
|
823 | + |
|
824 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
825 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
826 | + |
|
827 | + // Filter by node if provided |
|
828 | + if ($node !== null) { |
|
829 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
830 | + } |
|
831 | + |
|
832 | + $cursor = $qb->execute(); |
|
833 | + |
|
834 | + while($data = $cursor->fetch()) { |
|
835 | + $shares[] = $this->createShareObject($data); |
|
836 | + } |
|
837 | + $cursor->closeCursor(); |
|
838 | + |
|
839 | + |
|
840 | + return $shares; |
|
841 | + } |
|
842 | + |
|
843 | + /** |
|
844 | + * Get a share by token |
|
845 | + * |
|
846 | + * @param string $token |
|
847 | + * @return IShare |
|
848 | + * @throws ShareNotFound |
|
849 | + */ |
|
850 | + public function getShareByToken($token) { |
|
851 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
852 | + |
|
853 | + $cursor = $qb->select('*') |
|
854 | + ->from('share') |
|
855 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
856 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
857 | + ->execute(); |
|
858 | + |
|
859 | + $data = $cursor->fetch(); |
|
860 | + |
|
861 | + if ($data === false) { |
|
862 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
863 | + } |
|
864 | + |
|
865 | + try { |
|
866 | + $share = $this->createShareObject($data); |
|
867 | + } catch (InvalidShare $e) { |
|
868 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
869 | + } |
|
870 | + |
|
871 | + return $share; |
|
872 | + } |
|
873 | + |
|
874 | + /** |
|
875 | + * remove share from table |
|
876 | + * |
|
877 | + * @param string $shareId |
|
878 | + */ |
|
879 | + protected function removeShareFromTable($shareId) { |
|
880 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
881 | + $qb->delete('share') |
|
882 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
883 | + $qb->execute(); |
|
884 | + } |
|
885 | + |
|
886 | + /** |
|
887 | + * Create a share object from an database row |
|
888 | + * |
|
889 | + * @param array $data |
|
890 | + * @return IShare |
|
891 | + * @throws InvalidShare |
|
892 | + * @throws ShareNotFound |
|
893 | + */ |
|
894 | + protected function createShareObject($data) { |
|
895 | + |
|
896 | + $share = new Share($this->rootFolder, $this->userManager); |
|
897 | + $share->setId((int)$data['id']) |
|
898 | + ->setShareType((int)$data['share_type']) |
|
899 | + ->setPermissions((int)$data['permissions']) |
|
900 | + ->setTarget($data['file_target']) |
|
901 | + ->setMailSend((bool)$data['mail_send']) |
|
902 | + ->setToken($data['token']); |
|
903 | + |
|
904 | + $shareTime = new \DateTime(); |
|
905 | + $shareTime->setTimestamp((int)$data['stime']); |
|
906 | + $share->setShareTime($shareTime); |
|
907 | + $share->setSharedWith($data['share_with']); |
|
908 | + $share->setPassword($data['password']); |
|
909 | + |
|
910 | + if ($data['uid_initiator'] !== null) { |
|
911 | + $share->setShareOwner($data['uid_owner']); |
|
912 | + $share->setSharedBy($data['uid_initiator']); |
|
913 | + } else { |
|
914 | + //OLD SHARE |
|
915 | + $share->setSharedBy($data['uid_owner']); |
|
916 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
917 | + |
|
918 | + $owner = $path->getOwner(); |
|
919 | + $share->setShareOwner($owner->getUID()); |
|
920 | + } |
|
921 | + |
|
922 | + if ($data['expiration'] !== null) { |
|
923 | + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
924 | + if ($expiration !== false) { |
|
925 | + $share->setExpirationDate($expiration); |
|
926 | + } |
|
927 | + } |
|
928 | + |
|
929 | + $share->setNodeId((int)$data['file_source']); |
|
930 | + $share->setNodeType($data['item_type']); |
|
931 | + |
|
932 | + $share->setProviderId($this->identifier()); |
|
933 | + |
|
934 | + return $share; |
|
935 | + } |
|
936 | + |
|
937 | + /** |
|
938 | + * Get the node with file $id for $user |
|
939 | + * |
|
940 | + * @param string $userId |
|
941 | + * @param int $id |
|
942 | + * @return \OCP\Files\File|\OCP\Files\Folder |
|
943 | + * @throws InvalidShare |
|
944 | + */ |
|
945 | + private function getNode($userId, $id) { |
|
946 | + try { |
|
947 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
948 | + } catch (NoUserException $e) { |
|
949 | + throw new InvalidShare(); |
|
950 | + } |
|
951 | + |
|
952 | + $nodes = $userFolder->getById($id); |
|
953 | + |
|
954 | + if (empty($nodes)) { |
|
955 | + throw new InvalidShare(); |
|
956 | + } |
|
957 | + |
|
958 | + return $nodes[0]; |
|
959 | + } |
|
960 | + |
|
961 | + /** |
|
962 | + * A user is deleted from the system |
|
963 | + * So clean up the relevant shares. |
|
964 | + * |
|
965 | + * @param string $uid |
|
966 | + * @param int $shareType |
|
967 | + */ |
|
968 | + public function userDeleted($uid, $shareType) { |
|
969 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
970 | + |
|
971 | + $qb->delete('share') |
|
972 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
973 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
974 | + ->execute(); |
|
975 | + } |
|
976 | + |
|
977 | + /** |
|
978 | + * This provider does not support group shares |
|
979 | + * |
|
980 | + * @param string $gid |
|
981 | + */ |
|
982 | + public function groupDeleted($gid) { |
|
983 | + } |
|
984 | + |
|
985 | + /** |
|
986 | + * This provider does not support group shares |
|
987 | + * |
|
988 | + * @param string $uid |
|
989 | + * @param string $gid |
|
990 | + */ |
|
991 | + public function userDeletedFromGroup($uid, $gid) { |
|
992 | + } |
|
993 | + |
|
994 | + /** |
|
995 | + * get database row of a give share |
|
996 | + * |
|
997 | + * @param $id |
|
998 | + * @return array |
|
999 | + * @throws ShareNotFound |
|
1000 | + */ |
|
1001 | + protected function getRawShare($id) { |
|
1002 | + |
|
1003 | + // Now fetch the inserted share and create a complete share object |
|
1004 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1005 | + $qb->select('*') |
|
1006 | + ->from('share') |
|
1007 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
1008 | + |
|
1009 | + $cursor = $qb->execute(); |
|
1010 | + $data = $cursor->fetch(); |
|
1011 | + $cursor->closeCursor(); |
|
1012 | + |
|
1013 | + if ($data === false) { |
|
1014 | + throw new ShareNotFound; |
|
1015 | + } |
|
1016 | + |
|
1017 | + return $data; |
|
1018 | + } |
|
1019 | + |
|
1020 | + public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
1021 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1022 | + $qb->select('*') |
|
1023 | + ->from('share', 's') |
|
1024 | + ->andWhere($qb->expr()->orX( |
|
1025 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1026 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1027 | + )) |
|
1028 | + ->andWhere( |
|
1029 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
1030 | + ); |
|
1031 | + |
|
1032 | + /** |
|
1033 | + * Reshares for this user are shares where they are the owner. |
|
1034 | + */ |
|
1035 | + if ($reshares === false) { |
|
1036 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
1037 | + } else { |
|
1038 | + $qb->andWhere( |
|
1039 | + $qb->expr()->orX( |
|
1040 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
1041 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
1042 | + ) |
|
1043 | + ); |
|
1044 | + } |
|
1045 | + |
|
1046 | + $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
1047 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
1048 | + |
|
1049 | + $qb->orderBy('id'); |
|
1050 | + |
|
1051 | + $cursor = $qb->execute(); |
|
1052 | + $shares = []; |
|
1053 | + while ($data = $cursor->fetch()) { |
|
1054 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
1055 | + } |
|
1056 | + $cursor->closeCursor(); |
|
1057 | + |
|
1058 | + return $shares; |
|
1059 | + } |
|
1060 | + |
|
1061 | + /** |
|
1062 | + * @inheritdoc |
|
1063 | + */ |
|
1064 | + public function getAccessList($nodes, $currentAccess) { |
|
1065 | + $ids = []; |
|
1066 | + foreach ($nodes as $node) { |
|
1067 | + $ids[] = $node->getId(); |
|
1068 | + } |
|
1069 | + |
|
1070 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
1071 | + $qb->select('share_with') |
|
1072 | + ->from('share') |
|
1073 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
1074 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
1075 | + ->andWhere($qb->expr()->orX( |
|
1076 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
1077 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
1078 | + )) |
|
1079 | + ->setMaxResults(1); |
|
1080 | + $cursor = $qb->execute(); |
|
1081 | + |
|
1082 | + $mail = $cursor->fetch() !== false; |
|
1083 | + $cursor->closeCursor(); |
|
1084 | + |
|
1085 | + return ['public' => $mail]; |
|
1086 | + } |
|
1087 | 1087 | |
1088 | 1088 | } |
@@ -28,32 +28,32 @@ |
||
28 | 28 | |
29 | 29 | class SyncJob extends TimedJob { |
30 | 30 | |
31 | - /** @var SyncFederationAddressBooks */ |
|
32 | - protected $syncService; |
|
31 | + /** @var SyncFederationAddressBooks */ |
|
32 | + protected $syncService; |
|
33 | 33 | |
34 | - /** @var ILogger */ |
|
35 | - protected $logger; |
|
34 | + /** @var ILogger */ |
|
35 | + protected $logger; |
|
36 | 36 | |
37 | - /** |
|
38 | - * @param SyncFederationAddressBooks $syncService |
|
39 | - * @param ILogger $logger |
|
40 | - */ |
|
41 | - public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) { |
|
42 | - // Run once a day |
|
43 | - $this->setInterval(24 * 60 * 60); |
|
44 | - $this->syncService = $syncService; |
|
45 | - $this->logger = $logger; |
|
46 | - } |
|
37 | + /** |
|
38 | + * @param SyncFederationAddressBooks $syncService |
|
39 | + * @param ILogger $logger |
|
40 | + */ |
|
41 | + public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) { |
|
42 | + // Run once a day |
|
43 | + $this->setInterval(24 * 60 * 60); |
|
44 | + $this->syncService = $syncService; |
|
45 | + $this->logger = $logger; |
|
46 | + } |
|
47 | 47 | |
48 | - protected function run($argument) { |
|
49 | - $this->syncService->syncThemAll(function($url, $ex) { |
|
50 | - if ($ex instanceof \Exception) { |
|
51 | - $this->logger->logException($ex, [ |
|
52 | - 'message' => "Error while syncing $url.", |
|
53 | - 'level' => ILogger::ERROR, |
|
54 | - 'app' => 'fed-sync', |
|
55 | - ]); |
|
56 | - } |
|
57 | - }); |
|
58 | - } |
|
48 | + protected function run($argument) { |
|
49 | + $this->syncService->syncThemAll(function($url, $ex) { |
|
50 | + if ($ex instanceof \Exception) { |
|
51 | + $this->logger->logException($ex, [ |
|
52 | + 'message' => "Error while syncing $url.", |
|
53 | + 'level' => ILogger::ERROR, |
|
54 | + 'app' => 'fed-sync', |
|
55 | + ]); |
|
56 | + } |
|
57 | + }); |
|
58 | + } |
|
59 | 59 | } |
@@ -38,54 +38,54 @@ |
||
38 | 38 | |
39 | 39 | class AddServerMiddleware extends Middleware { |
40 | 40 | |
41 | - /** @var string */ |
|
42 | - protected $appName; |
|
41 | + /** @var string */ |
|
42 | + protected $appName; |
|
43 | 43 | |
44 | - /** @var IL10N */ |
|
45 | - protected $l; |
|
44 | + /** @var IL10N */ |
|
45 | + protected $l; |
|
46 | 46 | |
47 | - /** @var ILogger */ |
|
48 | - protected $logger; |
|
47 | + /** @var ILogger */ |
|
48 | + protected $logger; |
|
49 | 49 | |
50 | - /** |
|
51 | - * @param string $appName |
|
52 | - * @param IL10N $l |
|
53 | - * @param ILogger $logger |
|
54 | - */ |
|
55 | - public function __construct($appName, IL10N $l, ILogger $logger) { |
|
56 | - $this->appName = $appName; |
|
57 | - $this->l = $l; |
|
58 | - $this->logger = $logger; |
|
59 | - } |
|
50 | + /** |
|
51 | + * @param string $appName |
|
52 | + * @param IL10N $l |
|
53 | + * @param ILogger $logger |
|
54 | + */ |
|
55 | + public function __construct($appName, IL10N $l, ILogger $logger) { |
|
56 | + $this->appName = $appName; |
|
57 | + $this->l = $l; |
|
58 | + $this->logger = $logger; |
|
59 | + } |
|
60 | 60 | |
61 | - /** |
|
62 | - * Log error message and return a response which can be displayed to the user |
|
63 | - * |
|
64 | - * @param Controller $controller |
|
65 | - * @param string $methodName |
|
66 | - * @param \Exception $exception |
|
67 | - * @return JSONResponse |
|
68 | - * @throws \Exception |
|
69 | - */ |
|
70 | - public function afterException($controller, $methodName, \Exception $exception) { |
|
71 | - if (($controller instanceof SettingsController) === false) { |
|
72 | - throw $exception; |
|
73 | - } |
|
74 | - $this->logger->logException($exception, [ |
|
75 | - 'level' => ILogger::ERROR, |
|
76 | - 'app' => $this->appName, |
|
77 | - ]); |
|
78 | - if ($exception instanceof HintException) { |
|
79 | - $message = $exception->getHint(); |
|
80 | - } else { |
|
81 | - $message = $exception->getMessage(); |
|
82 | - } |
|
61 | + /** |
|
62 | + * Log error message and return a response which can be displayed to the user |
|
63 | + * |
|
64 | + * @param Controller $controller |
|
65 | + * @param string $methodName |
|
66 | + * @param \Exception $exception |
|
67 | + * @return JSONResponse |
|
68 | + * @throws \Exception |
|
69 | + */ |
|
70 | + public function afterException($controller, $methodName, \Exception $exception) { |
|
71 | + if (($controller instanceof SettingsController) === false) { |
|
72 | + throw $exception; |
|
73 | + } |
|
74 | + $this->logger->logException($exception, [ |
|
75 | + 'level' => ILogger::ERROR, |
|
76 | + 'app' => $this->appName, |
|
77 | + ]); |
|
78 | + if ($exception instanceof HintException) { |
|
79 | + $message = $exception->getHint(); |
|
80 | + } else { |
|
81 | + $message = $exception->getMessage(); |
|
82 | + } |
|
83 | 83 | |
84 | - return new JSONResponse( |
|
85 | - ['message' => $message], |
|
86 | - Http::STATUS_BAD_REQUEST |
|
87 | - ); |
|
84 | + return new JSONResponse( |
|
85 | + ['message' => $message], |
|
86 | + Http::STATUS_BAD_REQUEST |
|
87 | + ); |
|
88 | 88 | |
89 | - } |
|
89 | + } |
|
90 | 90 | |
91 | 91 | } |
@@ -41,252 +41,252 @@ |
||
41 | 41 | |
42 | 42 | class TrustedServers { |
43 | 43 | |
44 | - /** after a user list was exchanged at least once successfully */ |
|
45 | - const STATUS_OK = 1; |
|
46 | - /** waiting for shared secret or initial user list exchange */ |
|
47 | - const STATUS_PENDING = 2; |
|
48 | - /** something went wrong, misconfigured server, software bug,... user interaction needed */ |
|
49 | - const STATUS_FAILURE = 3; |
|
50 | - /** remote server revoked access */ |
|
51 | - const STATUS_ACCESS_REVOKED = 4; |
|
52 | - |
|
53 | - /** @var dbHandler */ |
|
54 | - private $dbHandler; |
|
55 | - |
|
56 | - /** @var IClientService */ |
|
57 | - private $httpClientService; |
|
58 | - |
|
59 | - /** @var ILogger */ |
|
60 | - private $logger; |
|
61 | - |
|
62 | - /** @var IJobList */ |
|
63 | - private $jobList; |
|
64 | - |
|
65 | - /** @var ISecureRandom */ |
|
66 | - private $secureRandom; |
|
67 | - |
|
68 | - /** @var IConfig */ |
|
69 | - private $config; |
|
70 | - |
|
71 | - /** @var EventDispatcherInterface */ |
|
72 | - private $dispatcher; |
|
73 | - |
|
74 | - /** @var ITimeFactory */ |
|
75 | - private $timeFactory; |
|
76 | - |
|
77 | - /** |
|
78 | - * @param DbHandler $dbHandler |
|
79 | - * @param IClientService $httpClientService |
|
80 | - * @param ILogger $logger |
|
81 | - * @param IJobList $jobList |
|
82 | - * @param ISecureRandom $secureRandom |
|
83 | - * @param IConfig $config |
|
84 | - * @param EventDispatcherInterface $dispatcher |
|
85 | - * @param ITimeFactory $timeFactory |
|
86 | - */ |
|
87 | - public function __construct( |
|
88 | - DbHandler $dbHandler, |
|
89 | - IClientService $httpClientService, |
|
90 | - ILogger $logger, |
|
91 | - IJobList $jobList, |
|
92 | - ISecureRandom $secureRandom, |
|
93 | - IConfig $config, |
|
94 | - EventDispatcherInterface $dispatcher, |
|
95 | - ITimeFactory $timeFactory |
|
96 | - ) { |
|
97 | - $this->dbHandler = $dbHandler; |
|
98 | - $this->httpClientService = $httpClientService; |
|
99 | - $this->logger = $logger; |
|
100 | - $this->jobList = $jobList; |
|
101 | - $this->secureRandom = $secureRandom; |
|
102 | - $this->config = $config; |
|
103 | - $this->dispatcher = $dispatcher; |
|
104 | - $this->timeFactory = $timeFactory; |
|
105 | - } |
|
106 | - |
|
107 | - /** |
|
108 | - * add server to the list of trusted servers |
|
109 | - * |
|
110 | - * @param $url |
|
111 | - * @return int server id |
|
112 | - */ |
|
113 | - public function addServer($url) { |
|
114 | - $url = $this->updateProtocol($url); |
|
115 | - $result = $this->dbHandler->addServer($url); |
|
116 | - if ($result) { |
|
117 | - $token = $this->secureRandom->generate(16); |
|
118 | - $this->dbHandler->addToken($url, $token); |
|
119 | - $this->jobList->add( |
|
120 | - RequestSharedSecret::class, |
|
121 | - [ |
|
122 | - 'url' => $url, |
|
123 | - 'token' => $token, |
|
124 | - 'created' => $this->timeFactory->getTime() |
|
125 | - ] |
|
126 | - ); |
|
127 | - } |
|
128 | - |
|
129 | - return $result; |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * enable/disable to automatically add servers to the list of trusted servers |
|
134 | - * once a federated share was created and accepted successfully |
|
135 | - * |
|
136 | - * @param bool $status |
|
137 | - */ |
|
138 | - public function setAutoAddServers($status) { |
|
139 | - $value = $status ? '1' : '0'; |
|
140 | - $this->config->setAppValue('federation', 'autoAddServers', $value); |
|
141 | - } |
|
142 | - |
|
143 | - /** |
|
144 | - * return if we automatically add servers to the list of trusted servers |
|
145 | - * once a federated share was created and accepted successfully |
|
146 | - * |
|
147 | - * @return bool |
|
148 | - */ |
|
149 | - public function getAutoAddServers() { |
|
150 | - $value = $this->config->getAppValue('federation', 'autoAddServers', '0'); |
|
151 | - return $value === '1'; |
|
152 | - } |
|
153 | - |
|
154 | - /** |
|
155 | - * get shared secret for the given server |
|
156 | - * |
|
157 | - * @param string $url |
|
158 | - * @return string |
|
159 | - */ |
|
160 | - public function getSharedSecret($url) { |
|
161 | - return $this->dbHandler->getSharedSecret($url); |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * add shared secret for the given server |
|
166 | - * |
|
167 | - * @param string $url |
|
168 | - * @param $sharedSecret |
|
169 | - */ |
|
170 | - public function addSharedSecret($url, $sharedSecret) { |
|
171 | - $this->dbHandler->addSharedSecret($url, $sharedSecret); |
|
172 | - } |
|
173 | - |
|
174 | - /** |
|
175 | - * remove server from the list of trusted servers |
|
176 | - * |
|
177 | - * @param int $id |
|
178 | - */ |
|
179 | - public function removeServer($id) { |
|
180 | - $server = $this->dbHandler->getServerById($id); |
|
181 | - $this->dbHandler->removeServer($id); |
|
182 | - $event = new GenericEvent($server['url_hash']); |
|
183 | - $this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event); |
|
184 | - } |
|
185 | - |
|
186 | - /** |
|
187 | - * get all trusted servers |
|
188 | - * |
|
189 | - * @return array |
|
190 | - */ |
|
191 | - public function getServers() { |
|
192 | - return $this->dbHandler->getAllServer(); |
|
193 | - } |
|
194 | - |
|
195 | - /** |
|
196 | - * check if given server is a trusted Nextcloud server |
|
197 | - * |
|
198 | - * @param string $url |
|
199 | - * @return bool |
|
200 | - */ |
|
201 | - public function isTrustedServer($url) { |
|
202 | - return $this->dbHandler->serverExists($url); |
|
203 | - } |
|
204 | - |
|
205 | - /** |
|
206 | - * set server status |
|
207 | - * |
|
208 | - * @param string $url |
|
209 | - * @param int $status |
|
210 | - */ |
|
211 | - public function setServerStatus($url, $status) { |
|
212 | - $this->dbHandler->setServerStatus($url, $status); |
|
213 | - } |
|
214 | - |
|
215 | - /** |
|
216 | - * @param string $url |
|
217 | - * @return int |
|
218 | - */ |
|
219 | - public function getServerStatus($url) { |
|
220 | - return $this->dbHandler->getServerStatus($url); |
|
221 | - } |
|
222 | - |
|
223 | - /** |
|
224 | - * check if URL point to a ownCloud/Nextcloud server |
|
225 | - * |
|
226 | - * @param string $url |
|
227 | - * @return bool |
|
228 | - */ |
|
229 | - public function isOwnCloudServer($url) { |
|
230 | - $isValidOwnCloud = false; |
|
231 | - $client = $this->httpClientService->newClient(); |
|
232 | - try { |
|
233 | - $result = $client->get( |
|
234 | - $url . '/status.php', |
|
235 | - [ |
|
236 | - 'timeout' => 3, |
|
237 | - 'connect_timeout' => 3, |
|
238 | - ] |
|
239 | - ); |
|
240 | - if ($result->getStatusCode() === Http::STATUS_OK) { |
|
241 | - $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); |
|
242 | - |
|
243 | - } |
|
244 | - } catch (\Exception $e) { |
|
245 | - \OC::$server->getLogger()->logException($e, [ |
|
246 | - 'message' => 'No Nextcloud server.', |
|
247 | - 'level' => ILogger::DEBUG, |
|
248 | - 'app' => 'federation', |
|
249 | - ]); |
|
250 | - return false; |
|
251 | - } |
|
252 | - |
|
253 | - return $isValidOwnCloud; |
|
254 | - } |
|
255 | - |
|
256 | - /** |
|
257 | - * check if ownCloud version is >= 9.0 |
|
258 | - * |
|
259 | - * @param $status |
|
260 | - * @return bool |
|
261 | - * @throws HintException |
|
262 | - */ |
|
263 | - protected function checkOwnCloudVersion($status) { |
|
264 | - $decoded = json_decode($status, true); |
|
265 | - if (!empty($decoded) && isset($decoded['version'])) { |
|
266 | - if (!version_compare($decoded['version'], '9.0.0', '>=')) { |
|
267 | - throw new HintException('Remote server version is too low. 9.0 is required.'); |
|
268 | - } |
|
269 | - return true; |
|
270 | - } |
|
271 | - return false; |
|
272 | - } |
|
273 | - |
|
274 | - /** |
|
275 | - * check if the URL contain a protocol, if not add https |
|
276 | - * |
|
277 | - * @param string $url |
|
278 | - * @return string |
|
279 | - */ |
|
280 | - protected function updateProtocol($url) { |
|
281 | - if ( |
|
282 | - strpos($url, 'https://') === 0 |
|
283 | - || strpos($url, 'http://') === 0 |
|
284 | - ) { |
|
285 | - |
|
286 | - return $url; |
|
287 | - |
|
288 | - } |
|
289 | - |
|
290 | - return 'https://' . $url; |
|
291 | - } |
|
44 | + /** after a user list was exchanged at least once successfully */ |
|
45 | + const STATUS_OK = 1; |
|
46 | + /** waiting for shared secret or initial user list exchange */ |
|
47 | + const STATUS_PENDING = 2; |
|
48 | + /** something went wrong, misconfigured server, software bug,... user interaction needed */ |
|
49 | + const STATUS_FAILURE = 3; |
|
50 | + /** remote server revoked access */ |
|
51 | + const STATUS_ACCESS_REVOKED = 4; |
|
52 | + |
|
53 | + /** @var dbHandler */ |
|
54 | + private $dbHandler; |
|
55 | + |
|
56 | + /** @var IClientService */ |
|
57 | + private $httpClientService; |
|
58 | + |
|
59 | + /** @var ILogger */ |
|
60 | + private $logger; |
|
61 | + |
|
62 | + /** @var IJobList */ |
|
63 | + private $jobList; |
|
64 | + |
|
65 | + /** @var ISecureRandom */ |
|
66 | + private $secureRandom; |
|
67 | + |
|
68 | + /** @var IConfig */ |
|
69 | + private $config; |
|
70 | + |
|
71 | + /** @var EventDispatcherInterface */ |
|
72 | + private $dispatcher; |
|
73 | + |
|
74 | + /** @var ITimeFactory */ |
|
75 | + private $timeFactory; |
|
76 | + |
|
77 | + /** |
|
78 | + * @param DbHandler $dbHandler |
|
79 | + * @param IClientService $httpClientService |
|
80 | + * @param ILogger $logger |
|
81 | + * @param IJobList $jobList |
|
82 | + * @param ISecureRandom $secureRandom |
|
83 | + * @param IConfig $config |
|
84 | + * @param EventDispatcherInterface $dispatcher |
|
85 | + * @param ITimeFactory $timeFactory |
|
86 | + */ |
|
87 | + public function __construct( |
|
88 | + DbHandler $dbHandler, |
|
89 | + IClientService $httpClientService, |
|
90 | + ILogger $logger, |
|
91 | + IJobList $jobList, |
|
92 | + ISecureRandom $secureRandom, |
|
93 | + IConfig $config, |
|
94 | + EventDispatcherInterface $dispatcher, |
|
95 | + ITimeFactory $timeFactory |
|
96 | + ) { |
|
97 | + $this->dbHandler = $dbHandler; |
|
98 | + $this->httpClientService = $httpClientService; |
|
99 | + $this->logger = $logger; |
|
100 | + $this->jobList = $jobList; |
|
101 | + $this->secureRandom = $secureRandom; |
|
102 | + $this->config = $config; |
|
103 | + $this->dispatcher = $dispatcher; |
|
104 | + $this->timeFactory = $timeFactory; |
|
105 | + } |
|
106 | + |
|
107 | + /** |
|
108 | + * add server to the list of trusted servers |
|
109 | + * |
|
110 | + * @param $url |
|
111 | + * @return int server id |
|
112 | + */ |
|
113 | + public function addServer($url) { |
|
114 | + $url = $this->updateProtocol($url); |
|
115 | + $result = $this->dbHandler->addServer($url); |
|
116 | + if ($result) { |
|
117 | + $token = $this->secureRandom->generate(16); |
|
118 | + $this->dbHandler->addToken($url, $token); |
|
119 | + $this->jobList->add( |
|
120 | + RequestSharedSecret::class, |
|
121 | + [ |
|
122 | + 'url' => $url, |
|
123 | + 'token' => $token, |
|
124 | + 'created' => $this->timeFactory->getTime() |
|
125 | + ] |
|
126 | + ); |
|
127 | + } |
|
128 | + |
|
129 | + return $result; |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * enable/disable to automatically add servers to the list of trusted servers |
|
134 | + * once a federated share was created and accepted successfully |
|
135 | + * |
|
136 | + * @param bool $status |
|
137 | + */ |
|
138 | + public function setAutoAddServers($status) { |
|
139 | + $value = $status ? '1' : '0'; |
|
140 | + $this->config->setAppValue('federation', 'autoAddServers', $value); |
|
141 | + } |
|
142 | + |
|
143 | + /** |
|
144 | + * return if we automatically add servers to the list of trusted servers |
|
145 | + * once a federated share was created and accepted successfully |
|
146 | + * |
|
147 | + * @return bool |
|
148 | + */ |
|
149 | + public function getAutoAddServers() { |
|
150 | + $value = $this->config->getAppValue('federation', 'autoAddServers', '0'); |
|
151 | + return $value === '1'; |
|
152 | + } |
|
153 | + |
|
154 | + /** |
|
155 | + * get shared secret for the given server |
|
156 | + * |
|
157 | + * @param string $url |
|
158 | + * @return string |
|
159 | + */ |
|
160 | + public function getSharedSecret($url) { |
|
161 | + return $this->dbHandler->getSharedSecret($url); |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * add shared secret for the given server |
|
166 | + * |
|
167 | + * @param string $url |
|
168 | + * @param $sharedSecret |
|
169 | + */ |
|
170 | + public function addSharedSecret($url, $sharedSecret) { |
|
171 | + $this->dbHandler->addSharedSecret($url, $sharedSecret); |
|
172 | + } |
|
173 | + |
|
174 | + /** |
|
175 | + * remove server from the list of trusted servers |
|
176 | + * |
|
177 | + * @param int $id |
|
178 | + */ |
|
179 | + public function removeServer($id) { |
|
180 | + $server = $this->dbHandler->getServerById($id); |
|
181 | + $this->dbHandler->removeServer($id); |
|
182 | + $event = new GenericEvent($server['url_hash']); |
|
183 | + $this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event); |
|
184 | + } |
|
185 | + |
|
186 | + /** |
|
187 | + * get all trusted servers |
|
188 | + * |
|
189 | + * @return array |
|
190 | + */ |
|
191 | + public function getServers() { |
|
192 | + return $this->dbHandler->getAllServer(); |
|
193 | + } |
|
194 | + |
|
195 | + /** |
|
196 | + * check if given server is a trusted Nextcloud server |
|
197 | + * |
|
198 | + * @param string $url |
|
199 | + * @return bool |
|
200 | + */ |
|
201 | + public function isTrustedServer($url) { |
|
202 | + return $this->dbHandler->serverExists($url); |
|
203 | + } |
|
204 | + |
|
205 | + /** |
|
206 | + * set server status |
|
207 | + * |
|
208 | + * @param string $url |
|
209 | + * @param int $status |
|
210 | + */ |
|
211 | + public function setServerStatus($url, $status) { |
|
212 | + $this->dbHandler->setServerStatus($url, $status); |
|
213 | + } |
|
214 | + |
|
215 | + /** |
|
216 | + * @param string $url |
|
217 | + * @return int |
|
218 | + */ |
|
219 | + public function getServerStatus($url) { |
|
220 | + return $this->dbHandler->getServerStatus($url); |
|
221 | + } |
|
222 | + |
|
223 | + /** |
|
224 | + * check if URL point to a ownCloud/Nextcloud server |
|
225 | + * |
|
226 | + * @param string $url |
|
227 | + * @return bool |
|
228 | + */ |
|
229 | + public function isOwnCloudServer($url) { |
|
230 | + $isValidOwnCloud = false; |
|
231 | + $client = $this->httpClientService->newClient(); |
|
232 | + try { |
|
233 | + $result = $client->get( |
|
234 | + $url . '/status.php', |
|
235 | + [ |
|
236 | + 'timeout' => 3, |
|
237 | + 'connect_timeout' => 3, |
|
238 | + ] |
|
239 | + ); |
|
240 | + if ($result->getStatusCode() === Http::STATUS_OK) { |
|
241 | + $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody()); |
|
242 | + |
|
243 | + } |
|
244 | + } catch (\Exception $e) { |
|
245 | + \OC::$server->getLogger()->logException($e, [ |
|
246 | + 'message' => 'No Nextcloud server.', |
|
247 | + 'level' => ILogger::DEBUG, |
|
248 | + 'app' => 'federation', |
|
249 | + ]); |
|
250 | + return false; |
|
251 | + } |
|
252 | + |
|
253 | + return $isValidOwnCloud; |
|
254 | + } |
|
255 | + |
|
256 | + /** |
|
257 | + * check if ownCloud version is >= 9.0 |
|
258 | + * |
|
259 | + * @param $status |
|
260 | + * @return bool |
|
261 | + * @throws HintException |
|
262 | + */ |
|
263 | + protected function checkOwnCloudVersion($status) { |
|
264 | + $decoded = json_decode($status, true); |
|
265 | + if (!empty($decoded) && isset($decoded['version'])) { |
|
266 | + if (!version_compare($decoded['version'], '9.0.0', '>=')) { |
|
267 | + throw new HintException('Remote server version is too low. 9.0 is required.'); |
|
268 | + } |
|
269 | + return true; |
|
270 | + } |
|
271 | + return false; |
|
272 | + } |
|
273 | + |
|
274 | + /** |
|
275 | + * check if the URL contain a protocol, if not add https |
|
276 | + * |
|
277 | + * @param string $url |
|
278 | + * @return string |
|
279 | + */ |
|
280 | + protected function updateProtocol($url) { |
|
281 | + if ( |
|
282 | + strpos($url, 'https://') === 0 |
|
283 | + || strpos($url, 'http://') === 0 |
|
284 | + ) { |
|
285 | + |
|
286 | + return $url; |
|
287 | + |
|
288 | + } |
|
289 | + |
|
290 | + return 'https://' . $url; |
|
291 | + } |
|
292 | 292 | } |
@@ -40,685 +40,685 @@ |
||
40 | 40 | |
41 | 41 | class KeyManager { |
42 | 42 | |
43 | - /** |
|
44 | - * @var Session |
|
45 | - */ |
|
46 | - protected $session; |
|
47 | - /** |
|
48 | - * @var IStorage |
|
49 | - */ |
|
50 | - private $keyStorage; |
|
51 | - /** |
|
52 | - * @var Crypt |
|
53 | - */ |
|
54 | - private $crypt; |
|
55 | - /** |
|
56 | - * @var string |
|
57 | - */ |
|
58 | - private $recoveryKeyId; |
|
59 | - /** |
|
60 | - * @var string |
|
61 | - */ |
|
62 | - private $publicShareKeyId; |
|
63 | - /** |
|
64 | - * @var string |
|
65 | - */ |
|
66 | - private $masterKeyId; |
|
67 | - /** |
|
68 | - * @var string UserID |
|
69 | - */ |
|
70 | - private $keyId; |
|
71 | - /** |
|
72 | - * @var string |
|
73 | - */ |
|
74 | - private $publicKeyId = 'publicKey'; |
|
75 | - /** |
|
76 | - * @var string |
|
77 | - */ |
|
78 | - private $privateKeyId = 'privateKey'; |
|
79 | - |
|
80 | - /** |
|
81 | - * @var string |
|
82 | - */ |
|
83 | - private $shareKeyId = 'shareKey'; |
|
84 | - |
|
85 | - /** |
|
86 | - * @var string |
|
87 | - */ |
|
88 | - private $fileKeyId = 'fileKey'; |
|
89 | - /** |
|
90 | - * @var IConfig |
|
91 | - */ |
|
92 | - private $config; |
|
93 | - /** |
|
94 | - * @var ILogger |
|
95 | - */ |
|
96 | - private $log; |
|
97 | - /** |
|
98 | - * @var Util |
|
99 | - */ |
|
100 | - private $util; |
|
101 | - |
|
102 | - /** |
|
103 | - * @param IStorage $keyStorage |
|
104 | - * @param Crypt $crypt |
|
105 | - * @param IConfig $config |
|
106 | - * @param IUserSession $userSession |
|
107 | - * @param Session $session |
|
108 | - * @param ILogger $log |
|
109 | - * @param Util $util |
|
110 | - */ |
|
111 | - public function __construct( |
|
112 | - IStorage $keyStorage, |
|
113 | - Crypt $crypt, |
|
114 | - IConfig $config, |
|
115 | - IUserSession $userSession, |
|
116 | - Session $session, |
|
117 | - ILogger $log, |
|
118 | - Util $util |
|
119 | - ) { |
|
120 | - |
|
121 | - $this->util = $util; |
|
122 | - $this->session = $session; |
|
123 | - $this->keyStorage = $keyStorage; |
|
124 | - $this->crypt = $crypt; |
|
125 | - $this->config = $config; |
|
126 | - $this->log = $log; |
|
127 | - |
|
128 | - $this->recoveryKeyId = $this->config->getAppValue('encryption', |
|
129 | - 'recoveryKeyId'); |
|
130 | - if (empty($this->recoveryKeyId)) { |
|
131 | - $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8); |
|
132 | - $this->config->setAppValue('encryption', |
|
133 | - 'recoveryKeyId', |
|
134 | - $this->recoveryKeyId); |
|
135 | - } |
|
136 | - |
|
137 | - $this->publicShareKeyId = $this->config->getAppValue('encryption', |
|
138 | - 'publicShareKeyId'); |
|
139 | - if (empty($this->publicShareKeyId)) { |
|
140 | - $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); |
|
141 | - $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); |
|
142 | - } |
|
143 | - |
|
144 | - $this->masterKeyId = $this->config->getAppValue('encryption', |
|
145 | - 'masterKeyId'); |
|
146 | - if (empty($this->masterKeyId)) { |
|
147 | - $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8); |
|
148 | - $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId); |
|
149 | - } |
|
150 | - |
|
151 | - $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; |
|
152 | - $this->log = $log; |
|
153 | - } |
|
154 | - |
|
155 | - /** |
|
156 | - * check if key pair for public link shares exists, if not we create one |
|
157 | - */ |
|
158 | - public function validateShareKey() { |
|
159 | - $shareKey = $this->getPublicShareKey(); |
|
160 | - if (empty($shareKey)) { |
|
161 | - $keyPair = $this->crypt->createKeyPair(); |
|
162 | - |
|
163 | - // Save public key |
|
164 | - $this->keyStorage->setSystemUserKey( |
|
165 | - $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'], |
|
166 | - Encryption::ID); |
|
167 | - |
|
168 | - // Encrypt private key empty passphrase |
|
169 | - $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], ''); |
|
170 | - $header = $this->crypt->generateHeader(); |
|
171 | - $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey); |
|
172 | - } |
|
173 | - } |
|
174 | - |
|
175 | - /** |
|
176 | - * check if a key pair for the master key exists, if not we create one |
|
177 | - */ |
|
178 | - public function validateMasterKey() { |
|
179 | - |
|
180 | - if ($this->util->isMasterKeyEnabled() === false) { |
|
181 | - return; |
|
182 | - } |
|
183 | - |
|
184 | - $publicMasterKey = $this->getPublicMasterKey(); |
|
185 | - if (empty($publicMasterKey)) { |
|
186 | - $keyPair = $this->crypt->createKeyPair(); |
|
187 | - |
|
188 | - // Save public key |
|
189 | - $this->keyStorage->setSystemUserKey( |
|
190 | - $this->masterKeyId . '.publicKey', $keyPair['publicKey'], |
|
191 | - Encryption::ID); |
|
192 | - |
|
193 | - // Encrypt private key with system password |
|
194 | - $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId); |
|
195 | - $header = $this->crypt->generateHeader(); |
|
196 | - $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey); |
|
197 | - } |
|
198 | - |
|
199 | - if (!$this->session->isPrivateKeySet()) { |
|
200 | - $masterKey = $this->getSystemPrivateKey($this->masterKeyId); |
|
201 | - $decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId); |
|
202 | - $this->session->setPrivateKey($decryptedMasterKey); |
|
203 | - } |
|
204 | - |
|
205 | - // after the encryption key is available we are ready to go |
|
206 | - $this->session->setStatus(Session::INIT_SUCCESSFUL); |
|
207 | - } |
|
208 | - |
|
209 | - /** |
|
210 | - * @return bool |
|
211 | - */ |
|
212 | - public function recoveryKeyExists() { |
|
213 | - $key = $this->getRecoveryKey(); |
|
214 | - return !empty($key); |
|
215 | - } |
|
216 | - |
|
217 | - /** |
|
218 | - * get recovery key |
|
219 | - * |
|
220 | - * @return string |
|
221 | - */ |
|
222 | - public function getRecoveryKey() { |
|
223 | - return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID); |
|
224 | - } |
|
225 | - |
|
226 | - /** |
|
227 | - * get recovery key ID |
|
228 | - * |
|
229 | - * @return string |
|
230 | - */ |
|
231 | - public function getRecoveryKeyId() { |
|
232 | - return $this->recoveryKeyId; |
|
233 | - } |
|
234 | - |
|
235 | - /** |
|
236 | - * @param string $password |
|
237 | - * @return bool |
|
238 | - */ |
|
239 | - public function checkRecoveryPassword($password) { |
|
240 | - $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID); |
|
241 | - $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password); |
|
242 | - |
|
243 | - if ($decryptedRecoveryKey) { |
|
244 | - return true; |
|
245 | - } |
|
246 | - return false; |
|
247 | - } |
|
248 | - |
|
249 | - /** |
|
250 | - * @param string $uid |
|
251 | - * @param string $password |
|
252 | - * @param string $keyPair |
|
253 | - * @return bool |
|
254 | - */ |
|
255 | - public function storeKeyPair($uid, $password, $keyPair) { |
|
256 | - // Save Public Key |
|
257 | - $this->setPublicKey($uid, $keyPair['publicKey']); |
|
258 | - |
|
259 | - $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid); |
|
260 | - |
|
261 | - $header = $this->crypt->generateHeader(); |
|
262 | - |
|
263 | - if ($encryptedKey) { |
|
264 | - $this->setPrivateKey($uid, $header . $encryptedKey); |
|
265 | - return true; |
|
266 | - } |
|
267 | - return false; |
|
268 | - } |
|
269 | - |
|
270 | - /** |
|
271 | - * @param string $password |
|
272 | - * @param array $keyPair |
|
273 | - * @return bool |
|
274 | - */ |
|
275 | - public function setRecoveryKey($password, $keyPair) { |
|
276 | - // Save Public Key |
|
277 | - $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId(). |
|
278 | - '.publicKey', |
|
279 | - $keyPair['publicKey'], |
|
280 | - Encryption::ID); |
|
281 | - |
|
282 | - $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password); |
|
283 | - $header = $this->crypt->generateHeader(); |
|
284 | - |
|
285 | - if ($encryptedKey) { |
|
286 | - $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey); |
|
287 | - return true; |
|
288 | - } |
|
289 | - return false; |
|
290 | - } |
|
291 | - |
|
292 | - /** |
|
293 | - * @param $userId |
|
294 | - * @param $key |
|
295 | - * @return bool |
|
296 | - */ |
|
297 | - public function setPublicKey($userId, $key) { |
|
298 | - return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID); |
|
299 | - } |
|
300 | - |
|
301 | - /** |
|
302 | - * @param $userId |
|
303 | - * @param string $key |
|
304 | - * @return bool |
|
305 | - */ |
|
306 | - public function setPrivateKey($userId, $key) { |
|
307 | - return $this->keyStorage->setUserKey($userId, |
|
308 | - $this->privateKeyId, |
|
309 | - $key, |
|
310 | - Encryption::ID); |
|
311 | - } |
|
312 | - |
|
313 | - /** |
|
314 | - * write file key to key storage |
|
315 | - * |
|
316 | - * @param string $path |
|
317 | - * @param string $key |
|
318 | - * @return boolean |
|
319 | - */ |
|
320 | - public function setFileKey($path, $key) { |
|
321 | - return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID); |
|
322 | - } |
|
323 | - |
|
324 | - /** |
|
325 | - * set all file keys (the file key and the corresponding share keys) |
|
326 | - * |
|
327 | - * @param string $path |
|
328 | - * @param array $keys |
|
329 | - */ |
|
330 | - public function setAllFileKeys($path, $keys) { |
|
331 | - $this->setFileKey($path, $keys['data']); |
|
332 | - foreach ($keys['keys'] as $uid => $keyFile) { |
|
333 | - $this->setShareKey($path, $uid, $keyFile); |
|
334 | - } |
|
335 | - } |
|
336 | - |
|
337 | - /** |
|
338 | - * write share key to the key storage |
|
339 | - * |
|
340 | - * @param string $path |
|
341 | - * @param string $uid |
|
342 | - * @param string $key |
|
343 | - * @return boolean |
|
344 | - */ |
|
345 | - public function setShareKey($path, $uid, $key) { |
|
346 | - $keyId = $uid . '.' . $this->shareKeyId; |
|
347 | - return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID); |
|
348 | - } |
|
349 | - |
|
350 | - /** |
|
351 | - * Decrypt private key and store it |
|
352 | - * |
|
353 | - * @param string $uid user id |
|
354 | - * @param string $passPhrase users password |
|
355 | - * @return boolean |
|
356 | - */ |
|
357 | - public function init($uid, $passPhrase) { |
|
358 | - |
|
359 | - $this->session->setStatus(Session::INIT_EXECUTED); |
|
360 | - |
|
361 | - try { |
|
362 | - if($this->util->isMasterKeyEnabled()) { |
|
363 | - $uid = $this->getMasterKeyId(); |
|
364 | - $passPhrase = $this->getMasterKeyPassword(); |
|
365 | - $privateKey = $this->getSystemPrivateKey($uid); |
|
366 | - } else { |
|
367 | - $privateKey = $this->getPrivateKey($uid); |
|
368 | - } |
|
369 | - $privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid); |
|
370 | - } catch (PrivateKeyMissingException $e) { |
|
371 | - return false; |
|
372 | - } catch (DecryptionFailedException $e) { |
|
373 | - return false; |
|
374 | - } catch (\Exception $e) { |
|
375 | - $this->log->logException($e, [ |
|
376 | - 'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.', |
|
377 | - 'level' => ILogger::WARN, |
|
378 | - 'app' => 'encryption', |
|
379 | - ]); |
|
380 | - return false; |
|
381 | - } |
|
382 | - |
|
383 | - if ($privateKey) { |
|
384 | - $this->session->setPrivateKey($privateKey); |
|
385 | - $this->session->setStatus(Session::INIT_SUCCESSFUL); |
|
386 | - return true; |
|
387 | - } |
|
388 | - |
|
389 | - return false; |
|
390 | - } |
|
391 | - |
|
392 | - /** |
|
393 | - * @param $userId |
|
394 | - * @return string |
|
395 | - * @throws PrivateKeyMissingException |
|
396 | - */ |
|
397 | - public function getPrivateKey($userId) { |
|
398 | - $privateKey = $this->keyStorage->getUserKey($userId, |
|
399 | - $this->privateKeyId, Encryption::ID); |
|
400 | - |
|
401 | - if (strlen($privateKey) !== 0) { |
|
402 | - return $privateKey; |
|
403 | - } |
|
404 | - throw new PrivateKeyMissingException($userId); |
|
405 | - } |
|
406 | - |
|
407 | - /** |
|
408 | - * @param string $path |
|
409 | - * @param $uid |
|
410 | - * @return string |
|
411 | - */ |
|
412 | - public function getFileKey($path, $uid) { |
|
413 | - if ($uid === '') { |
|
414 | - $uid = null; |
|
415 | - } |
|
416 | - $publicAccess = is_null($uid); |
|
417 | - $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID); |
|
418 | - |
|
419 | - if (empty($encryptedFileKey)) { |
|
420 | - return ''; |
|
421 | - } |
|
422 | - |
|
423 | - if ($this->util->isMasterKeyEnabled()) { |
|
424 | - $uid = $this->getMasterKeyId(); |
|
425 | - $shareKey = $this->getShareKey($path, $uid); |
|
426 | - if ($publicAccess) { |
|
427 | - $privateKey = $this->getSystemPrivateKey($uid); |
|
428 | - $privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid); |
|
429 | - } else { |
|
430 | - // when logged in, the master key is already decrypted in the session |
|
431 | - $privateKey = $this->session->getPrivateKey(); |
|
432 | - } |
|
433 | - } else if ($publicAccess) { |
|
434 | - // use public share key for public links |
|
435 | - $uid = $this->getPublicShareKeyId(); |
|
436 | - $shareKey = $this->getShareKey($path, $uid); |
|
437 | - $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID); |
|
438 | - $privateKey = $this->crypt->decryptPrivateKey($privateKey); |
|
439 | - } else { |
|
440 | - $shareKey = $this->getShareKey($path, $uid); |
|
441 | - $privateKey = $this->session->getPrivateKey(); |
|
442 | - } |
|
443 | - |
|
444 | - if ($encryptedFileKey && $shareKey && $privateKey) { |
|
445 | - return $this->crypt->multiKeyDecrypt($encryptedFileKey, |
|
446 | - $shareKey, |
|
447 | - $privateKey); |
|
448 | - } |
|
449 | - |
|
450 | - return ''; |
|
451 | - } |
|
452 | - |
|
453 | - /** |
|
454 | - * Get the current version of a file |
|
455 | - * |
|
456 | - * @param string $path |
|
457 | - * @param View $view |
|
458 | - * @return int |
|
459 | - */ |
|
460 | - public function getVersion($path, View $view) { |
|
461 | - $fileInfo = $view->getFileInfo($path); |
|
462 | - if($fileInfo === false) { |
|
463 | - return 0; |
|
464 | - } |
|
465 | - return $fileInfo->getEncryptedVersion(); |
|
466 | - } |
|
467 | - |
|
468 | - /** |
|
469 | - * Set the current version of a file |
|
470 | - * |
|
471 | - * @param string $path |
|
472 | - * @param int $version |
|
473 | - * @param View $view |
|
474 | - */ |
|
475 | - public function setVersion($path, $version, View $view) { |
|
476 | - $fileInfo= $view->getFileInfo($path); |
|
477 | - |
|
478 | - if($fileInfo !== false) { |
|
479 | - $cache = $fileInfo->getStorage()->getCache(); |
|
480 | - $cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]); |
|
481 | - } |
|
482 | - } |
|
483 | - |
|
484 | - /** |
|
485 | - * get the encrypted file key |
|
486 | - * |
|
487 | - * @param string $path |
|
488 | - * @return string |
|
489 | - */ |
|
490 | - public function getEncryptedFileKey($path) { |
|
491 | - $encryptedFileKey = $this->keyStorage->getFileKey($path, |
|
492 | - $this->fileKeyId, Encryption::ID); |
|
493 | - |
|
494 | - return $encryptedFileKey; |
|
495 | - } |
|
496 | - |
|
497 | - /** |
|
498 | - * delete share key |
|
499 | - * |
|
500 | - * @param string $path |
|
501 | - * @param string $keyId |
|
502 | - * @return boolean |
|
503 | - */ |
|
504 | - public function deleteShareKey($path, $keyId) { |
|
505 | - return $this->keyStorage->deleteFileKey( |
|
506 | - $path, |
|
507 | - $keyId . '.' . $this->shareKeyId, |
|
508 | - Encryption::ID); |
|
509 | - } |
|
510 | - |
|
511 | - |
|
512 | - /** |
|
513 | - * @param $path |
|
514 | - * @param $uid |
|
515 | - * @return mixed |
|
516 | - */ |
|
517 | - public function getShareKey($path, $uid) { |
|
518 | - $keyId = $uid . '.' . $this->shareKeyId; |
|
519 | - return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID); |
|
520 | - } |
|
521 | - |
|
522 | - /** |
|
523 | - * check if user has a private and a public key |
|
524 | - * |
|
525 | - * @param string $userId |
|
526 | - * @return bool |
|
527 | - * @throws PrivateKeyMissingException |
|
528 | - * @throws PublicKeyMissingException |
|
529 | - */ |
|
530 | - public function userHasKeys($userId) { |
|
531 | - $privateKey = $publicKey = true; |
|
532 | - $exception = null; |
|
533 | - |
|
534 | - try { |
|
535 | - $this->getPrivateKey($userId); |
|
536 | - } catch (PrivateKeyMissingException $e) { |
|
537 | - $privateKey = false; |
|
538 | - $exception = $e; |
|
539 | - } |
|
540 | - try { |
|
541 | - $this->getPublicKey($userId); |
|
542 | - } catch (PublicKeyMissingException $e) { |
|
543 | - $publicKey = false; |
|
544 | - $exception = $e; |
|
545 | - } |
|
546 | - |
|
547 | - if ($privateKey && $publicKey) { |
|
548 | - return true; |
|
549 | - } elseif (!$privateKey && !$publicKey) { |
|
550 | - return false; |
|
551 | - } else { |
|
552 | - throw $exception; |
|
553 | - } |
|
554 | - } |
|
555 | - |
|
556 | - /** |
|
557 | - * @param $userId |
|
558 | - * @return mixed |
|
559 | - * @throws PublicKeyMissingException |
|
560 | - */ |
|
561 | - public function getPublicKey($userId) { |
|
562 | - $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID); |
|
563 | - |
|
564 | - if (strlen($publicKey) !== 0) { |
|
565 | - return $publicKey; |
|
566 | - } |
|
567 | - throw new PublicKeyMissingException($userId); |
|
568 | - } |
|
569 | - |
|
570 | - public function getPublicShareKeyId() { |
|
571 | - return $this->publicShareKeyId; |
|
572 | - } |
|
573 | - |
|
574 | - /** |
|
575 | - * get public key for public link shares |
|
576 | - * |
|
577 | - * @return string |
|
578 | - */ |
|
579 | - public function getPublicShareKey() { |
|
580 | - return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID); |
|
581 | - } |
|
582 | - |
|
583 | - /** |
|
584 | - * @param string $purpose |
|
585 | - * @param string $uid |
|
586 | - */ |
|
587 | - public function backupUserKeys($purpose, $uid) { |
|
588 | - $this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid); |
|
589 | - } |
|
590 | - |
|
591 | - /** |
|
592 | - * creat a backup of the users private and public key and then delete it |
|
593 | - * |
|
594 | - * @param string $uid |
|
595 | - */ |
|
596 | - public function deleteUserKeys($uid) { |
|
597 | - $this->deletePublicKey($uid); |
|
598 | - $this->deletePrivateKey($uid); |
|
599 | - } |
|
600 | - |
|
601 | - /** |
|
602 | - * @param $uid |
|
603 | - * @return bool |
|
604 | - */ |
|
605 | - public function deletePublicKey($uid) { |
|
606 | - return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID); |
|
607 | - } |
|
608 | - |
|
609 | - /** |
|
610 | - * @param string $uid |
|
611 | - * @return bool |
|
612 | - */ |
|
613 | - private function deletePrivateKey($uid) { |
|
614 | - return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID); |
|
615 | - } |
|
616 | - |
|
617 | - /** |
|
618 | - * @param string $path |
|
619 | - * @return bool |
|
620 | - */ |
|
621 | - public function deleteAllFileKeys($path) { |
|
622 | - return $this->keyStorage->deleteAllFileKeys($path); |
|
623 | - } |
|
624 | - |
|
625 | - /** |
|
626 | - * @param array $userIds |
|
627 | - * @return array |
|
628 | - * @throws PublicKeyMissingException |
|
629 | - */ |
|
630 | - public function getPublicKeys(array $userIds) { |
|
631 | - $keys = []; |
|
632 | - |
|
633 | - foreach ($userIds as $userId) { |
|
634 | - try { |
|
635 | - $keys[$userId] = $this->getPublicKey($userId); |
|
636 | - } catch (PublicKeyMissingException $e) { |
|
637 | - continue; |
|
638 | - } |
|
639 | - } |
|
640 | - |
|
641 | - return $keys; |
|
642 | - |
|
643 | - } |
|
644 | - |
|
645 | - /** |
|
646 | - * @param string $keyId |
|
647 | - * @return string returns openssl key |
|
648 | - */ |
|
649 | - public function getSystemPrivateKey($keyId) { |
|
650 | - return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID); |
|
651 | - } |
|
652 | - |
|
653 | - /** |
|
654 | - * @param string $keyId |
|
655 | - * @param string $key |
|
656 | - * @return string returns openssl key |
|
657 | - */ |
|
658 | - public function setSystemPrivateKey($keyId, $key) { |
|
659 | - return $this->keyStorage->setSystemUserKey( |
|
660 | - $keyId . '.' . $this->privateKeyId, |
|
661 | - $key, |
|
662 | - Encryption::ID); |
|
663 | - } |
|
664 | - |
|
665 | - /** |
|
666 | - * add system keys such as the public share key and the recovery key |
|
667 | - * |
|
668 | - * @param array $accessList |
|
669 | - * @param array $publicKeys |
|
670 | - * @param string $uid |
|
671 | - * @return array |
|
672 | - * @throws PublicKeyMissingException |
|
673 | - */ |
|
674 | - public function addSystemKeys(array $accessList, array $publicKeys, $uid) { |
|
675 | - if (!empty($accessList['public'])) { |
|
676 | - $publicShareKey = $this->getPublicShareKey(); |
|
677 | - if (empty($publicShareKey)) { |
|
678 | - throw new PublicKeyMissingException($this->getPublicShareKeyId()); |
|
679 | - } |
|
680 | - $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey; |
|
681 | - } |
|
682 | - |
|
683 | - if ($this->recoveryKeyExists() && |
|
684 | - $this->util->isRecoveryEnabledForUser($uid)) { |
|
685 | - |
|
686 | - $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey(); |
|
687 | - } |
|
688 | - |
|
689 | - return $publicKeys; |
|
690 | - } |
|
691 | - |
|
692 | - /** |
|
693 | - * get master key password |
|
694 | - * |
|
695 | - * @return string |
|
696 | - * @throws \Exception |
|
697 | - */ |
|
698 | - public function getMasterKeyPassword() { |
|
699 | - $password = $this->config->getSystemValue('secret'); |
|
700 | - if (empty($password)){ |
|
701 | - throw new \Exception('Can not get secret from Nextcloud instance'); |
|
702 | - } |
|
703 | - |
|
704 | - return $password; |
|
705 | - } |
|
706 | - |
|
707 | - /** |
|
708 | - * return master key id |
|
709 | - * |
|
710 | - * @return string |
|
711 | - */ |
|
712 | - public function getMasterKeyId() { |
|
713 | - return $this->masterKeyId; |
|
714 | - } |
|
715 | - |
|
716 | - /** |
|
717 | - * get public master key |
|
718 | - * |
|
719 | - * @return string |
|
720 | - */ |
|
721 | - public function getPublicMasterKey() { |
|
722 | - return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID); |
|
723 | - } |
|
43 | + /** |
|
44 | + * @var Session |
|
45 | + */ |
|
46 | + protected $session; |
|
47 | + /** |
|
48 | + * @var IStorage |
|
49 | + */ |
|
50 | + private $keyStorage; |
|
51 | + /** |
|
52 | + * @var Crypt |
|
53 | + */ |
|
54 | + private $crypt; |
|
55 | + /** |
|
56 | + * @var string |
|
57 | + */ |
|
58 | + private $recoveryKeyId; |
|
59 | + /** |
|
60 | + * @var string |
|
61 | + */ |
|
62 | + private $publicShareKeyId; |
|
63 | + /** |
|
64 | + * @var string |
|
65 | + */ |
|
66 | + private $masterKeyId; |
|
67 | + /** |
|
68 | + * @var string UserID |
|
69 | + */ |
|
70 | + private $keyId; |
|
71 | + /** |
|
72 | + * @var string |
|
73 | + */ |
|
74 | + private $publicKeyId = 'publicKey'; |
|
75 | + /** |
|
76 | + * @var string |
|
77 | + */ |
|
78 | + private $privateKeyId = 'privateKey'; |
|
79 | + |
|
80 | + /** |
|
81 | + * @var string |
|
82 | + */ |
|
83 | + private $shareKeyId = 'shareKey'; |
|
84 | + |
|
85 | + /** |
|
86 | + * @var string |
|
87 | + */ |
|
88 | + private $fileKeyId = 'fileKey'; |
|
89 | + /** |
|
90 | + * @var IConfig |
|
91 | + */ |
|
92 | + private $config; |
|
93 | + /** |
|
94 | + * @var ILogger |
|
95 | + */ |
|
96 | + private $log; |
|
97 | + /** |
|
98 | + * @var Util |
|
99 | + */ |
|
100 | + private $util; |
|
101 | + |
|
102 | + /** |
|
103 | + * @param IStorage $keyStorage |
|
104 | + * @param Crypt $crypt |
|
105 | + * @param IConfig $config |
|
106 | + * @param IUserSession $userSession |
|
107 | + * @param Session $session |
|
108 | + * @param ILogger $log |
|
109 | + * @param Util $util |
|
110 | + */ |
|
111 | + public function __construct( |
|
112 | + IStorage $keyStorage, |
|
113 | + Crypt $crypt, |
|
114 | + IConfig $config, |
|
115 | + IUserSession $userSession, |
|
116 | + Session $session, |
|
117 | + ILogger $log, |
|
118 | + Util $util |
|
119 | + ) { |
|
120 | + |
|
121 | + $this->util = $util; |
|
122 | + $this->session = $session; |
|
123 | + $this->keyStorage = $keyStorage; |
|
124 | + $this->crypt = $crypt; |
|
125 | + $this->config = $config; |
|
126 | + $this->log = $log; |
|
127 | + |
|
128 | + $this->recoveryKeyId = $this->config->getAppValue('encryption', |
|
129 | + 'recoveryKeyId'); |
|
130 | + if (empty($this->recoveryKeyId)) { |
|
131 | + $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8); |
|
132 | + $this->config->setAppValue('encryption', |
|
133 | + 'recoveryKeyId', |
|
134 | + $this->recoveryKeyId); |
|
135 | + } |
|
136 | + |
|
137 | + $this->publicShareKeyId = $this->config->getAppValue('encryption', |
|
138 | + 'publicShareKeyId'); |
|
139 | + if (empty($this->publicShareKeyId)) { |
|
140 | + $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); |
|
141 | + $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); |
|
142 | + } |
|
143 | + |
|
144 | + $this->masterKeyId = $this->config->getAppValue('encryption', |
|
145 | + 'masterKeyId'); |
|
146 | + if (empty($this->masterKeyId)) { |
|
147 | + $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8); |
|
148 | + $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId); |
|
149 | + } |
|
150 | + |
|
151 | + $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false; |
|
152 | + $this->log = $log; |
|
153 | + } |
|
154 | + |
|
155 | + /** |
|
156 | + * check if key pair for public link shares exists, if not we create one |
|
157 | + */ |
|
158 | + public function validateShareKey() { |
|
159 | + $shareKey = $this->getPublicShareKey(); |
|
160 | + if (empty($shareKey)) { |
|
161 | + $keyPair = $this->crypt->createKeyPair(); |
|
162 | + |
|
163 | + // Save public key |
|
164 | + $this->keyStorage->setSystemUserKey( |
|
165 | + $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'], |
|
166 | + Encryption::ID); |
|
167 | + |
|
168 | + // Encrypt private key empty passphrase |
|
169 | + $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], ''); |
|
170 | + $header = $this->crypt->generateHeader(); |
|
171 | + $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey); |
|
172 | + } |
|
173 | + } |
|
174 | + |
|
175 | + /** |
|
176 | + * check if a key pair for the master key exists, if not we create one |
|
177 | + */ |
|
178 | + public function validateMasterKey() { |
|
179 | + |
|
180 | + if ($this->util->isMasterKeyEnabled() === false) { |
|
181 | + return; |
|
182 | + } |
|
183 | + |
|
184 | + $publicMasterKey = $this->getPublicMasterKey(); |
|
185 | + if (empty($publicMasterKey)) { |
|
186 | + $keyPair = $this->crypt->createKeyPair(); |
|
187 | + |
|
188 | + // Save public key |
|
189 | + $this->keyStorage->setSystemUserKey( |
|
190 | + $this->masterKeyId . '.publicKey', $keyPair['publicKey'], |
|
191 | + Encryption::ID); |
|
192 | + |
|
193 | + // Encrypt private key with system password |
|
194 | + $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId); |
|
195 | + $header = $this->crypt->generateHeader(); |
|
196 | + $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey); |
|
197 | + } |
|
198 | + |
|
199 | + if (!$this->session->isPrivateKeySet()) { |
|
200 | + $masterKey = $this->getSystemPrivateKey($this->masterKeyId); |
|
201 | + $decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId); |
|
202 | + $this->session->setPrivateKey($decryptedMasterKey); |
|
203 | + } |
|
204 | + |
|
205 | + // after the encryption key is available we are ready to go |
|
206 | + $this->session->setStatus(Session::INIT_SUCCESSFUL); |
|
207 | + } |
|
208 | + |
|
209 | + /** |
|
210 | + * @return bool |
|
211 | + */ |
|
212 | + public function recoveryKeyExists() { |
|
213 | + $key = $this->getRecoveryKey(); |
|
214 | + return !empty($key); |
|
215 | + } |
|
216 | + |
|
217 | + /** |
|
218 | + * get recovery key |
|
219 | + * |
|
220 | + * @return string |
|
221 | + */ |
|
222 | + public function getRecoveryKey() { |
|
223 | + return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID); |
|
224 | + } |
|
225 | + |
|
226 | + /** |
|
227 | + * get recovery key ID |
|
228 | + * |
|
229 | + * @return string |
|
230 | + */ |
|
231 | + public function getRecoveryKeyId() { |
|
232 | + return $this->recoveryKeyId; |
|
233 | + } |
|
234 | + |
|
235 | + /** |
|
236 | + * @param string $password |
|
237 | + * @return bool |
|
238 | + */ |
|
239 | + public function checkRecoveryPassword($password) { |
|
240 | + $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID); |
|
241 | + $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password); |
|
242 | + |
|
243 | + if ($decryptedRecoveryKey) { |
|
244 | + return true; |
|
245 | + } |
|
246 | + return false; |
|
247 | + } |
|
248 | + |
|
249 | + /** |
|
250 | + * @param string $uid |
|
251 | + * @param string $password |
|
252 | + * @param string $keyPair |
|
253 | + * @return bool |
|
254 | + */ |
|
255 | + public function storeKeyPair($uid, $password, $keyPair) { |
|
256 | + // Save Public Key |
|
257 | + $this->setPublicKey($uid, $keyPair['publicKey']); |
|
258 | + |
|
259 | + $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid); |
|
260 | + |
|
261 | + $header = $this->crypt->generateHeader(); |
|
262 | + |
|
263 | + if ($encryptedKey) { |
|
264 | + $this->setPrivateKey($uid, $header . $encryptedKey); |
|
265 | + return true; |
|
266 | + } |
|
267 | + return false; |
|
268 | + } |
|
269 | + |
|
270 | + /** |
|
271 | + * @param string $password |
|
272 | + * @param array $keyPair |
|
273 | + * @return bool |
|
274 | + */ |
|
275 | + public function setRecoveryKey($password, $keyPair) { |
|
276 | + // Save Public Key |
|
277 | + $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId(). |
|
278 | + '.publicKey', |
|
279 | + $keyPair['publicKey'], |
|
280 | + Encryption::ID); |
|
281 | + |
|
282 | + $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password); |
|
283 | + $header = $this->crypt->generateHeader(); |
|
284 | + |
|
285 | + if ($encryptedKey) { |
|
286 | + $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey); |
|
287 | + return true; |
|
288 | + } |
|
289 | + return false; |
|
290 | + } |
|
291 | + |
|
292 | + /** |
|
293 | + * @param $userId |
|
294 | + * @param $key |
|
295 | + * @return bool |
|
296 | + */ |
|
297 | + public function setPublicKey($userId, $key) { |
|
298 | + return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID); |
|
299 | + } |
|
300 | + |
|
301 | + /** |
|
302 | + * @param $userId |
|
303 | + * @param string $key |
|
304 | + * @return bool |
|
305 | + */ |
|
306 | + public function setPrivateKey($userId, $key) { |
|
307 | + return $this->keyStorage->setUserKey($userId, |
|
308 | + $this->privateKeyId, |
|
309 | + $key, |
|
310 | + Encryption::ID); |
|
311 | + } |
|
312 | + |
|
313 | + /** |
|
314 | + * write file key to key storage |
|
315 | + * |
|
316 | + * @param string $path |
|
317 | + * @param string $key |
|
318 | + * @return boolean |
|
319 | + */ |
|
320 | + public function setFileKey($path, $key) { |
|
321 | + return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID); |
|
322 | + } |
|
323 | + |
|
324 | + /** |
|
325 | + * set all file keys (the file key and the corresponding share keys) |
|
326 | + * |
|
327 | + * @param string $path |
|
328 | + * @param array $keys |
|
329 | + */ |
|
330 | + public function setAllFileKeys($path, $keys) { |
|
331 | + $this->setFileKey($path, $keys['data']); |
|
332 | + foreach ($keys['keys'] as $uid => $keyFile) { |
|
333 | + $this->setShareKey($path, $uid, $keyFile); |
|
334 | + } |
|
335 | + } |
|
336 | + |
|
337 | + /** |
|
338 | + * write share key to the key storage |
|
339 | + * |
|
340 | + * @param string $path |
|
341 | + * @param string $uid |
|
342 | + * @param string $key |
|
343 | + * @return boolean |
|
344 | + */ |
|
345 | + public function setShareKey($path, $uid, $key) { |
|
346 | + $keyId = $uid . '.' . $this->shareKeyId; |
|
347 | + return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID); |
|
348 | + } |
|
349 | + |
|
350 | + /** |
|
351 | + * Decrypt private key and store it |
|
352 | + * |
|
353 | + * @param string $uid user id |
|
354 | + * @param string $passPhrase users password |
|
355 | + * @return boolean |
|
356 | + */ |
|
357 | + public function init($uid, $passPhrase) { |
|
358 | + |
|
359 | + $this->session->setStatus(Session::INIT_EXECUTED); |
|
360 | + |
|
361 | + try { |
|
362 | + if($this->util->isMasterKeyEnabled()) { |
|
363 | + $uid = $this->getMasterKeyId(); |
|
364 | + $passPhrase = $this->getMasterKeyPassword(); |
|
365 | + $privateKey = $this->getSystemPrivateKey($uid); |
|
366 | + } else { |
|
367 | + $privateKey = $this->getPrivateKey($uid); |
|
368 | + } |
|
369 | + $privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid); |
|
370 | + } catch (PrivateKeyMissingException $e) { |
|
371 | + return false; |
|
372 | + } catch (DecryptionFailedException $e) { |
|
373 | + return false; |
|
374 | + } catch (\Exception $e) { |
|
375 | + $this->log->logException($e, [ |
|
376 | + 'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.', |
|
377 | + 'level' => ILogger::WARN, |
|
378 | + 'app' => 'encryption', |
|
379 | + ]); |
|
380 | + return false; |
|
381 | + } |
|
382 | + |
|
383 | + if ($privateKey) { |
|
384 | + $this->session->setPrivateKey($privateKey); |
|
385 | + $this->session->setStatus(Session::INIT_SUCCESSFUL); |
|
386 | + return true; |
|
387 | + } |
|
388 | + |
|
389 | + return false; |
|
390 | + } |
|
391 | + |
|
392 | + /** |
|
393 | + * @param $userId |
|
394 | + * @return string |
|
395 | + * @throws PrivateKeyMissingException |
|
396 | + */ |
|
397 | + public function getPrivateKey($userId) { |
|
398 | + $privateKey = $this->keyStorage->getUserKey($userId, |
|
399 | + $this->privateKeyId, Encryption::ID); |
|
400 | + |
|
401 | + if (strlen($privateKey) !== 0) { |
|
402 | + return $privateKey; |
|
403 | + } |
|
404 | + throw new PrivateKeyMissingException($userId); |
|
405 | + } |
|
406 | + |
|
407 | + /** |
|
408 | + * @param string $path |
|
409 | + * @param $uid |
|
410 | + * @return string |
|
411 | + */ |
|
412 | + public function getFileKey($path, $uid) { |
|
413 | + if ($uid === '') { |
|
414 | + $uid = null; |
|
415 | + } |
|
416 | + $publicAccess = is_null($uid); |
|
417 | + $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID); |
|
418 | + |
|
419 | + if (empty($encryptedFileKey)) { |
|
420 | + return ''; |
|
421 | + } |
|
422 | + |
|
423 | + if ($this->util->isMasterKeyEnabled()) { |
|
424 | + $uid = $this->getMasterKeyId(); |
|
425 | + $shareKey = $this->getShareKey($path, $uid); |
|
426 | + if ($publicAccess) { |
|
427 | + $privateKey = $this->getSystemPrivateKey($uid); |
|
428 | + $privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid); |
|
429 | + } else { |
|
430 | + // when logged in, the master key is already decrypted in the session |
|
431 | + $privateKey = $this->session->getPrivateKey(); |
|
432 | + } |
|
433 | + } else if ($publicAccess) { |
|
434 | + // use public share key for public links |
|
435 | + $uid = $this->getPublicShareKeyId(); |
|
436 | + $shareKey = $this->getShareKey($path, $uid); |
|
437 | + $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID); |
|
438 | + $privateKey = $this->crypt->decryptPrivateKey($privateKey); |
|
439 | + } else { |
|
440 | + $shareKey = $this->getShareKey($path, $uid); |
|
441 | + $privateKey = $this->session->getPrivateKey(); |
|
442 | + } |
|
443 | + |
|
444 | + if ($encryptedFileKey && $shareKey && $privateKey) { |
|
445 | + return $this->crypt->multiKeyDecrypt($encryptedFileKey, |
|
446 | + $shareKey, |
|
447 | + $privateKey); |
|
448 | + } |
|
449 | + |
|
450 | + return ''; |
|
451 | + } |
|
452 | + |
|
453 | + /** |
|
454 | + * Get the current version of a file |
|
455 | + * |
|
456 | + * @param string $path |
|
457 | + * @param View $view |
|
458 | + * @return int |
|
459 | + */ |
|
460 | + public function getVersion($path, View $view) { |
|
461 | + $fileInfo = $view->getFileInfo($path); |
|
462 | + if($fileInfo === false) { |
|
463 | + return 0; |
|
464 | + } |
|
465 | + return $fileInfo->getEncryptedVersion(); |
|
466 | + } |
|
467 | + |
|
468 | + /** |
|
469 | + * Set the current version of a file |
|
470 | + * |
|
471 | + * @param string $path |
|
472 | + * @param int $version |
|
473 | + * @param View $view |
|
474 | + */ |
|
475 | + public function setVersion($path, $version, View $view) { |
|
476 | + $fileInfo= $view->getFileInfo($path); |
|
477 | + |
|
478 | + if($fileInfo !== false) { |
|
479 | + $cache = $fileInfo->getStorage()->getCache(); |
|
480 | + $cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]); |
|
481 | + } |
|
482 | + } |
|
483 | + |
|
484 | + /** |
|
485 | + * get the encrypted file key |
|
486 | + * |
|
487 | + * @param string $path |
|
488 | + * @return string |
|
489 | + */ |
|
490 | + public function getEncryptedFileKey($path) { |
|
491 | + $encryptedFileKey = $this->keyStorage->getFileKey($path, |
|
492 | + $this->fileKeyId, Encryption::ID); |
|
493 | + |
|
494 | + return $encryptedFileKey; |
|
495 | + } |
|
496 | + |
|
497 | + /** |
|
498 | + * delete share key |
|
499 | + * |
|
500 | + * @param string $path |
|
501 | + * @param string $keyId |
|
502 | + * @return boolean |
|
503 | + */ |
|
504 | + public function deleteShareKey($path, $keyId) { |
|
505 | + return $this->keyStorage->deleteFileKey( |
|
506 | + $path, |
|
507 | + $keyId . '.' . $this->shareKeyId, |
|
508 | + Encryption::ID); |
|
509 | + } |
|
510 | + |
|
511 | + |
|
512 | + /** |
|
513 | + * @param $path |
|
514 | + * @param $uid |
|
515 | + * @return mixed |
|
516 | + */ |
|
517 | + public function getShareKey($path, $uid) { |
|
518 | + $keyId = $uid . '.' . $this->shareKeyId; |
|
519 | + return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID); |
|
520 | + } |
|
521 | + |
|
522 | + /** |
|
523 | + * check if user has a private and a public key |
|
524 | + * |
|
525 | + * @param string $userId |
|
526 | + * @return bool |
|
527 | + * @throws PrivateKeyMissingException |
|
528 | + * @throws PublicKeyMissingException |
|
529 | + */ |
|
530 | + public function userHasKeys($userId) { |
|
531 | + $privateKey = $publicKey = true; |
|
532 | + $exception = null; |
|
533 | + |
|
534 | + try { |
|
535 | + $this->getPrivateKey($userId); |
|
536 | + } catch (PrivateKeyMissingException $e) { |
|
537 | + $privateKey = false; |
|
538 | + $exception = $e; |
|
539 | + } |
|
540 | + try { |
|
541 | + $this->getPublicKey($userId); |
|
542 | + } catch (PublicKeyMissingException $e) { |
|
543 | + $publicKey = false; |
|
544 | + $exception = $e; |
|
545 | + } |
|
546 | + |
|
547 | + if ($privateKey && $publicKey) { |
|
548 | + return true; |
|
549 | + } elseif (!$privateKey && !$publicKey) { |
|
550 | + return false; |
|
551 | + } else { |
|
552 | + throw $exception; |
|
553 | + } |
|
554 | + } |
|
555 | + |
|
556 | + /** |
|
557 | + * @param $userId |
|
558 | + * @return mixed |
|
559 | + * @throws PublicKeyMissingException |
|
560 | + */ |
|
561 | + public function getPublicKey($userId) { |
|
562 | + $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID); |
|
563 | + |
|
564 | + if (strlen($publicKey) !== 0) { |
|
565 | + return $publicKey; |
|
566 | + } |
|
567 | + throw new PublicKeyMissingException($userId); |
|
568 | + } |
|
569 | + |
|
570 | + public function getPublicShareKeyId() { |
|
571 | + return $this->publicShareKeyId; |
|
572 | + } |
|
573 | + |
|
574 | + /** |
|
575 | + * get public key for public link shares |
|
576 | + * |
|
577 | + * @return string |
|
578 | + */ |
|
579 | + public function getPublicShareKey() { |
|
580 | + return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID); |
|
581 | + } |
|
582 | + |
|
583 | + /** |
|
584 | + * @param string $purpose |
|
585 | + * @param string $uid |
|
586 | + */ |
|
587 | + public function backupUserKeys($purpose, $uid) { |
|
588 | + $this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid); |
|
589 | + } |
|
590 | + |
|
591 | + /** |
|
592 | + * creat a backup of the users private and public key and then delete it |
|
593 | + * |
|
594 | + * @param string $uid |
|
595 | + */ |
|
596 | + public function deleteUserKeys($uid) { |
|
597 | + $this->deletePublicKey($uid); |
|
598 | + $this->deletePrivateKey($uid); |
|
599 | + } |
|
600 | + |
|
601 | + /** |
|
602 | + * @param $uid |
|
603 | + * @return bool |
|
604 | + */ |
|
605 | + public function deletePublicKey($uid) { |
|
606 | + return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID); |
|
607 | + } |
|
608 | + |
|
609 | + /** |
|
610 | + * @param string $uid |
|
611 | + * @return bool |
|
612 | + */ |
|
613 | + private function deletePrivateKey($uid) { |
|
614 | + return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID); |
|
615 | + } |
|
616 | + |
|
617 | + /** |
|
618 | + * @param string $path |
|
619 | + * @return bool |
|
620 | + */ |
|
621 | + public function deleteAllFileKeys($path) { |
|
622 | + return $this->keyStorage->deleteAllFileKeys($path); |
|
623 | + } |
|
624 | + |
|
625 | + /** |
|
626 | + * @param array $userIds |
|
627 | + * @return array |
|
628 | + * @throws PublicKeyMissingException |
|
629 | + */ |
|
630 | + public function getPublicKeys(array $userIds) { |
|
631 | + $keys = []; |
|
632 | + |
|
633 | + foreach ($userIds as $userId) { |
|
634 | + try { |
|
635 | + $keys[$userId] = $this->getPublicKey($userId); |
|
636 | + } catch (PublicKeyMissingException $e) { |
|
637 | + continue; |
|
638 | + } |
|
639 | + } |
|
640 | + |
|
641 | + return $keys; |
|
642 | + |
|
643 | + } |
|
644 | + |
|
645 | + /** |
|
646 | + * @param string $keyId |
|
647 | + * @return string returns openssl key |
|
648 | + */ |
|
649 | + public function getSystemPrivateKey($keyId) { |
|
650 | + return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID); |
|
651 | + } |
|
652 | + |
|
653 | + /** |
|
654 | + * @param string $keyId |
|
655 | + * @param string $key |
|
656 | + * @return string returns openssl key |
|
657 | + */ |
|
658 | + public function setSystemPrivateKey($keyId, $key) { |
|
659 | + return $this->keyStorage->setSystemUserKey( |
|
660 | + $keyId . '.' . $this->privateKeyId, |
|
661 | + $key, |
|
662 | + Encryption::ID); |
|
663 | + } |
|
664 | + |
|
665 | + /** |
|
666 | + * add system keys such as the public share key and the recovery key |
|
667 | + * |
|
668 | + * @param array $accessList |
|
669 | + * @param array $publicKeys |
|
670 | + * @param string $uid |
|
671 | + * @return array |
|
672 | + * @throws PublicKeyMissingException |
|
673 | + */ |
|
674 | + public function addSystemKeys(array $accessList, array $publicKeys, $uid) { |
|
675 | + if (!empty($accessList['public'])) { |
|
676 | + $publicShareKey = $this->getPublicShareKey(); |
|
677 | + if (empty($publicShareKey)) { |
|
678 | + throw new PublicKeyMissingException($this->getPublicShareKeyId()); |
|
679 | + } |
|
680 | + $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey; |
|
681 | + } |
|
682 | + |
|
683 | + if ($this->recoveryKeyExists() && |
|
684 | + $this->util->isRecoveryEnabledForUser($uid)) { |
|
685 | + |
|
686 | + $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey(); |
|
687 | + } |
|
688 | + |
|
689 | + return $publicKeys; |
|
690 | + } |
|
691 | + |
|
692 | + /** |
|
693 | + * get master key password |
|
694 | + * |
|
695 | + * @return string |
|
696 | + * @throws \Exception |
|
697 | + */ |
|
698 | + public function getMasterKeyPassword() { |
|
699 | + $password = $this->config->getSystemValue('secret'); |
|
700 | + if (empty($password)){ |
|
701 | + throw new \Exception('Can not get secret from Nextcloud instance'); |
|
702 | + } |
|
703 | + |
|
704 | + return $password; |
|
705 | + } |
|
706 | + |
|
707 | + /** |
|
708 | + * return master key id |
|
709 | + * |
|
710 | + * @return string |
|
711 | + */ |
|
712 | + public function getMasterKeyId() { |
|
713 | + return $this->masterKeyId; |
|
714 | + } |
|
715 | + |
|
716 | + /** |
|
717 | + * get public master key |
|
718 | + * |
|
719 | + * @return string |
|
720 | + */ |
|
721 | + public function getPublicMasterKey() { |
|
722 | + return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID); |
|
723 | + } |
|
724 | 724 | } |
@@ -128,7 +128,7 @@ discard block |
||
128 | 128 | $this->recoveryKeyId = $this->config->getAppValue('encryption', |
129 | 129 | 'recoveryKeyId'); |
130 | 130 | if (empty($this->recoveryKeyId)) { |
131 | - $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8); |
|
131 | + $this->recoveryKeyId = 'recoveryKey_'.substr(md5(time()), 0, 8); |
|
132 | 132 | $this->config->setAppValue('encryption', |
133 | 133 | 'recoveryKeyId', |
134 | 134 | $this->recoveryKeyId); |
@@ -137,14 +137,14 @@ discard block |
||
137 | 137 | $this->publicShareKeyId = $this->config->getAppValue('encryption', |
138 | 138 | 'publicShareKeyId'); |
139 | 139 | if (empty($this->publicShareKeyId)) { |
140 | - $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8); |
|
140 | + $this->publicShareKeyId = 'pubShare_'.substr(md5(time()), 0, 8); |
|
141 | 141 | $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId); |
142 | 142 | } |
143 | 143 | |
144 | 144 | $this->masterKeyId = $this->config->getAppValue('encryption', |
145 | 145 | 'masterKeyId'); |
146 | 146 | if (empty($this->masterKeyId)) { |
147 | - $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8); |
|
147 | + $this->masterKeyId = 'master_'.substr(md5(time()), 0, 8); |
|
148 | 148 | $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId); |
149 | 149 | } |
150 | 150 | |
@@ -162,13 +162,13 @@ discard block |
||
162 | 162 | |
163 | 163 | // Save public key |
164 | 164 | $this->keyStorage->setSystemUserKey( |
165 | - $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'], |
|
165 | + $this->publicShareKeyId.'.publicKey', $keyPair['publicKey'], |
|
166 | 166 | Encryption::ID); |
167 | 167 | |
168 | 168 | // Encrypt private key empty passphrase |
169 | 169 | $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], ''); |
170 | 170 | $header = $this->crypt->generateHeader(); |
171 | - $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey); |
|
171 | + $this->setSystemPrivateKey($this->publicShareKeyId, $header.$encryptedKey); |
|
172 | 172 | } |
173 | 173 | } |
174 | 174 | |
@@ -187,13 +187,13 @@ discard block |
||
187 | 187 | |
188 | 188 | // Save public key |
189 | 189 | $this->keyStorage->setSystemUserKey( |
190 | - $this->masterKeyId . '.publicKey', $keyPair['publicKey'], |
|
190 | + $this->masterKeyId.'.publicKey', $keyPair['publicKey'], |
|
191 | 191 | Encryption::ID); |
192 | 192 | |
193 | 193 | // Encrypt private key with system password |
194 | 194 | $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId); |
195 | 195 | $header = $this->crypt->generateHeader(); |
196 | - $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey); |
|
196 | + $this->setSystemPrivateKey($this->masterKeyId, $header.$encryptedKey); |
|
197 | 197 | } |
198 | 198 | |
199 | 199 | if (!$this->session->isPrivateKeySet()) { |
@@ -220,7 +220,7 @@ discard block |
||
220 | 220 | * @return string |
221 | 221 | */ |
222 | 222 | public function getRecoveryKey() { |
223 | - return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID); |
|
223 | + return $this->keyStorage->getSystemUserKey($this->recoveryKeyId.'.publicKey', Encryption::ID); |
|
224 | 224 | } |
225 | 225 | |
226 | 226 | /** |
@@ -237,7 +237,7 @@ discard block |
||
237 | 237 | * @return bool |
238 | 238 | */ |
239 | 239 | public function checkRecoveryPassword($password) { |
240 | - $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID); |
|
240 | + $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId.'.privateKey', Encryption::ID); |
|
241 | 241 | $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password); |
242 | 242 | |
243 | 243 | if ($decryptedRecoveryKey) { |
@@ -261,7 +261,7 @@ discard block |
||
261 | 261 | $header = $this->crypt->generateHeader(); |
262 | 262 | |
263 | 263 | if ($encryptedKey) { |
264 | - $this->setPrivateKey($uid, $header . $encryptedKey); |
|
264 | + $this->setPrivateKey($uid, $header.$encryptedKey); |
|
265 | 265 | return true; |
266 | 266 | } |
267 | 267 | return false; |
@@ -283,7 +283,7 @@ discard block |
||
283 | 283 | $header = $this->crypt->generateHeader(); |
284 | 284 | |
285 | 285 | if ($encryptedKey) { |
286 | - $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey); |
|
286 | + $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header.$encryptedKey); |
|
287 | 287 | return true; |
288 | 288 | } |
289 | 289 | return false; |
@@ -343,7 +343,7 @@ discard block |
||
343 | 343 | * @return boolean |
344 | 344 | */ |
345 | 345 | public function setShareKey($path, $uid, $key) { |
346 | - $keyId = $uid . '.' . $this->shareKeyId; |
|
346 | + $keyId = $uid.'.'.$this->shareKeyId; |
|
347 | 347 | return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID); |
348 | 348 | } |
349 | 349 | |
@@ -359,7 +359,7 @@ discard block |
||
359 | 359 | $this->session->setStatus(Session::INIT_EXECUTED); |
360 | 360 | |
361 | 361 | try { |
362 | - if($this->util->isMasterKeyEnabled()) { |
|
362 | + if ($this->util->isMasterKeyEnabled()) { |
|
363 | 363 | $uid = $this->getMasterKeyId(); |
364 | 364 | $passPhrase = $this->getMasterKeyPassword(); |
365 | 365 | $privateKey = $this->getSystemPrivateKey($uid); |
@@ -373,7 +373,7 @@ discard block |
||
373 | 373 | return false; |
374 | 374 | } catch (\Exception $e) { |
375 | 375 | $this->log->logException($e, [ |
376 | - 'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.', |
|
376 | + 'message' => 'Could not decrypt the private key from user "'.$uid.'"" during login. Assume password change on the user back-end.', |
|
377 | 377 | 'level' => ILogger::WARN, |
378 | 378 | 'app' => 'encryption', |
379 | 379 | ]); |
@@ -434,7 +434,7 @@ discard block |
||
434 | 434 | // use public share key for public links |
435 | 435 | $uid = $this->getPublicShareKeyId(); |
436 | 436 | $shareKey = $this->getShareKey($path, $uid); |
437 | - $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID); |
|
437 | + $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId.'.privateKey', Encryption::ID); |
|
438 | 438 | $privateKey = $this->crypt->decryptPrivateKey($privateKey); |
439 | 439 | } else { |
440 | 440 | $shareKey = $this->getShareKey($path, $uid); |
@@ -459,7 +459,7 @@ discard block |
||
459 | 459 | */ |
460 | 460 | public function getVersion($path, View $view) { |
461 | 461 | $fileInfo = $view->getFileInfo($path); |
462 | - if($fileInfo === false) { |
|
462 | + if ($fileInfo === false) { |
|
463 | 463 | return 0; |
464 | 464 | } |
465 | 465 | return $fileInfo->getEncryptedVersion(); |
@@ -473,9 +473,9 @@ discard block |
||
473 | 473 | * @param View $view |
474 | 474 | */ |
475 | 475 | public function setVersion($path, $version, View $view) { |
476 | - $fileInfo= $view->getFileInfo($path); |
|
476 | + $fileInfo = $view->getFileInfo($path); |
|
477 | 477 | |
478 | - if($fileInfo !== false) { |
|
478 | + if ($fileInfo !== false) { |
|
479 | 479 | $cache = $fileInfo->getStorage()->getCache(); |
480 | 480 | $cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]); |
481 | 481 | } |
@@ -504,7 +504,7 @@ discard block |
||
504 | 504 | public function deleteShareKey($path, $keyId) { |
505 | 505 | return $this->keyStorage->deleteFileKey( |
506 | 506 | $path, |
507 | - $keyId . '.' . $this->shareKeyId, |
|
507 | + $keyId.'.'.$this->shareKeyId, |
|
508 | 508 | Encryption::ID); |
509 | 509 | } |
510 | 510 | |
@@ -515,7 +515,7 @@ discard block |
||
515 | 515 | * @return mixed |
516 | 516 | */ |
517 | 517 | public function getShareKey($path, $uid) { |
518 | - $keyId = $uid . '.' . $this->shareKeyId; |
|
518 | + $keyId = $uid.'.'.$this->shareKeyId; |
|
519 | 519 | return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID); |
520 | 520 | } |
521 | 521 | |
@@ -577,7 +577,7 @@ discard block |
||
577 | 577 | * @return string |
578 | 578 | */ |
579 | 579 | public function getPublicShareKey() { |
580 | - return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID); |
|
580 | + return $this->keyStorage->getSystemUserKey($this->publicShareKeyId.'.publicKey', Encryption::ID); |
|
581 | 581 | } |
582 | 582 | |
583 | 583 | /** |
@@ -647,7 +647,7 @@ discard block |
||
647 | 647 | * @return string returns openssl key |
648 | 648 | */ |
649 | 649 | public function getSystemPrivateKey($keyId) { |
650 | - return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID); |
|
650 | + return $this->keyStorage->getSystemUserKey($keyId.'.'.$this->privateKeyId, Encryption::ID); |
|
651 | 651 | } |
652 | 652 | |
653 | 653 | /** |
@@ -657,7 +657,7 @@ discard block |
||
657 | 657 | */ |
658 | 658 | public function setSystemPrivateKey($keyId, $key) { |
659 | 659 | return $this->keyStorage->setSystemUserKey( |
660 | - $keyId . '.' . $this->privateKeyId, |
|
660 | + $keyId.'.'.$this->privateKeyId, |
|
661 | 661 | $key, |
662 | 662 | Encryption::ID); |
663 | 663 | } |
@@ -697,7 +697,7 @@ discard block |
||
697 | 697 | */ |
698 | 698 | public function getMasterKeyPassword() { |
699 | 699 | $password = $this->config->getSystemValue('secret'); |
700 | - if (empty($password)){ |
|
700 | + if (empty($password)) { |
|
701 | 701 | throw new \Exception('Can not get secret from Nextcloud instance'); |
702 | 702 | } |
703 | 703 | |
@@ -719,6 +719,6 @@ discard block |
||
719 | 719 | * @return string |
720 | 720 | */ |
721 | 721 | public function getPublicMasterKey() { |
722 | - return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID); |
|
722 | + return $this->keyStorage->getSystemUserKey($this->masterKeyId.'.publicKey', Encryption::ID); |
|
723 | 723 | } |
724 | 724 | } |
@@ -68,998 +68,998 @@ |
||
68 | 68 | * OC_autoload! |
69 | 69 | */ |
70 | 70 | class OC { |
71 | - /** |
|
72 | - * Associative array for autoloading. classname => filename |
|
73 | - */ |
|
74 | - public static $CLASSPATH = array(); |
|
75 | - /** |
|
76 | - * The installation path for Nextcloud on the server (e.g. /srv/http/nextcloud) |
|
77 | - */ |
|
78 | - public static $SERVERROOT = ''; |
|
79 | - /** |
|
80 | - * the current request path relative to the Nextcloud root (e.g. files/index.php) |
|
81 | - */ |
|
82 | - private static $SUBURI = ''; |
|
83 | - /** |
|
84 | - * the Nextcloud root path for http requests (e.g. nextcloud/) |
|
85 | - */ |
|
86 | - public static $WEBROOT = ''; |
|
87 | - /** |
|
88 | - * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and |
|
89 | - * web path in 'url' |
|
90 | - */ |
|
91 | - public static $APPSROOTS = array(); |
|
92 | - |
|
93 | - /** |
|
94 | - * @var string |
|
95 | - */ |
|
96 | - public static $configDir; |
|
97 | - |
|
98 | - /** |
|
99 | - * requested app |
|
100 | - */ |
|
101 | - public static $REQUESTEDAPP = ''; |
|
102 | - |
|
103 | - /** |
|
104 | - * check if Nextcloud runs in cli mode |
|
105 | - */ |
|
106 | - public static $CLI = false; |
|
107 | - |
|
108 | - /** |
|
109 | - * @var \OC\Autoloader $loader |
|
110 | - */ |
|
111 | - public static $loader = null; |
|
112 | - |
|
113 | - /** @var \Composer\Autoload\ClassLoader $composerAutoloader */ |
|
114 | - public static $composerAutoloader = null; |
|
115 | - |
|
116 | - /** |
|
117 | - * @var \OC\Server |
|
118 | - */ |
|
119 | - public static $server = null; |
|
120 | - |
|
121 | - /** |
|
122 | - * @var \OC\Config |
|
123 | - */ |
|
124 | - private static $config = null; |
|
125 | - |
|
126 | - /** |
|
127 | - * @throws \RuntimeException when the 3rdparty directory is missing or |
|
128 | - * the app path list is empty or contains an invalid path |
|
129 | - */ |
|
130 | - public static function initPaths() { |
|
131 | - if(defined('PHPUNIT_CONFIG_DIR')) { |
|
132 | - self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/'; |
|
133 | - } elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) { |
|
134 | - self::$configDir = OC::$SERVERROOT . '/tests/config/'; |
|
135 | - } elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { |
|
136 | - self::$configDir = rtrim($dir, '/') . '/'; |
|
137 | - } else { |
|
138 | - self::$configDir = OC::$SERVERROOT . '/config/'; |
|
139 | - } |
|
140 | - self::$config = new \OC\Config(self::$configDir); |
|
141 | - |
|
142 | - OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT))); |
|
143 | - /** |
|
144 | - * FIXME: The following lines are required because we can't yet instantiate |
|
145 | - * \OC::$server->getRequest() since \OC::$server does not yet exist. |
|
146 | - */ |
|
147 | - $params = [ |
|
148 | - 'server' => [ |
|
149 | - 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], |
|
150 | - 'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'], |
|
151 | - ], |
|
152 | - ]; |
|
153 | - $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config))); |
|
154 | - $scriptName = $fakeRequest->getScriptName(); |
|
155 | - if (substr($scriptName, -1) == '/') { |
|
156 | - $scriptName .= 'index.php'; |
|
157 | - //make sure suburi follows the same rules as scriptName |
|
158 | - if (substr(OC::$SUBURI, -9) != 'index.php') { |
|
159 | - if (substr(OC::$SUBURI, -1) != '/') { |
|
160 | - OC::$SUBURI = OC::$SUBURI . '/'; |
|
161 | - } |
|
162 | - OC::$SUBURI = OC::$SUBURI . 'index.php'; |
|
163 | - } |
|
164 | - } |
|
165 | - |
|
166 | - |
|
167 | - if (OC::$CLI) { |
|
168 | - OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); |
|
169 | - } else { |
|
170 | - if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) { |
|
171 | - OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI)); |
|
172 | - |
|
173 | - if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') { |
|
174 | - OC::$WEBROOT = '/' . OC::$WEBROOT; |
|
175 | - } |
|
176 | - } else { |
|
177 | - // The scriptName is not ending with OC::$SUBURI |
|
178 | - // This most likely means that we are calling from CLI. |
|
179 | - // However some cron jobs still need to generate |
|
180 | - // a web URL, so we use overwritewebroot as a fallback. |
|
181 | - OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); |
|
182 | - } |
|
183 | - |
|
184 | - // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing |
|
185 | - // slash which is required by URL generation. |
|
186 | - if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT && |
|
187 | - substr($_SERVER['REQUEST_URI'], -1) !== '/') { |
|
188 | - header('Location: '.\OC::$WEBROOT.'/'); |
|
189 | - exit(); |
|
190 | - } |
|
191 | - } |
|
192 | - |
|
193 | - // search the apps folder |
|
194 | - $config_paths = self::$config->getValue('apps_paths', array()); |
|
195 | - if (!empty($config_paths)) { |
|
196 | - foreach ($config_paths as $paths) { |
|
197 | - if (isset($paths['url']) && isset($paths['path'])) { |
|
198 | - $paths['url'] = rtrim($paths['url'], '/'); |
|
199 | - $paths['path'] = rtrim($paths['path'], '/'); |
|
200 | - OC::$APPSROOTS[] = $paths; |
|
201 | - } |
|
202 | - } |
|
203 | - } elseif (file_exists(OC::$SERVERROOT . '/apps')) { |
|
204 | - OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true); |
|
205 | - } elseif (file_exists(OC::$SERVERROOT . '/../apps')) { |
|
206 | - OC::$APPSROOTS[] = array( |
|
207 | - 'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps', |
|
208 | - 'url' => '/apps', |
|
209 | - 'writable' => true |
|
210 | - ); |
|
211 | - } |
|
212 | - |
|
213 | - if (empty(OC::$APPSROOTS)) { |
|
214 | - throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder' |
|
215 | - . ' or the folder above. You can also configure the location in the config.php file.'); |
|
216 | - } |
|
217 | - $paths = array(); |
|
218 | - foreach (OC::$APPSROOTS as $path) { |
|
219 | - $paths[] = $path['path']; |
|
220 | - if (!is_dir($path['path'])) { |
|
221 | - throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the' |
|
222 | - . ' Nextcloud folder or the folder above. You can also configure the location in the' |
|
223 | - . ' config.php file.', $path['path'])); |
|
224 | - } |
|
225 | - } |
|
226 | - |
|
227 | - // set the right include path |
|
228 | - set_include_path( |
|
229 | - implode(PATH_SEPARATOR, $paths) |
|
230 | - ); |
|
231 | - } |
|
232 | - |
|
233 | - public static function checkConfig() { |
|
234 | - $l = \OC::$server->getL10N('lib'); |
|
235 | - |
|
236 | - // Create config if it does not already exist |
|
237 | - $configFilePath = self::$configDir .'/config.php'; |
|
238 | - if(!file_exists($configFilePath)) { |
|
239 | - @touch($configFilePath); |
|
240 | - } |
|
241 | - |
|
242 | - // Check if config is writable |
|
243 | - $configFileWritable = is_writable($configFilePath); |
|
244 | - if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled() |
|
245 | - || !$configFileWritable && \OCP\Util::needUpgrade()) { |
|
246 | - |
|
247 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
248 | - |
|
249 | - if (self::$CLI) { |
|
250 | - echo $l->t('Cannot write into "config" directory!')."\n"; |
|
251 | - echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n"; |
|
252 | - echo "\n"; |
|
253 | - echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n"; |
|
254 | - exit; |
|
255 | - } else { |
|
256 | - OC_Template::printErrorPage( |
|
257 | - $l->t('Cannot write into "config" directory!'), |
|
258 | - $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s', |
|
259 | - [ $urlGenerator->linkToDocs('admin-dir_permissions') ]) |
|
260 | - ); |
|
261 | - } |
|
262 | - } |
|
263 | - } |
|
264 | - |
|
265 | - public static function checkInstalled() { |
|
266 | - if (defined('OC_CONSOLE')) { |
|
267 | - return; |
|
268 | - } |
|
269 | - // Redirect to installer if not installed |
|
270 | - if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') { |
|
271 | - if (OC::$CLI) { |
|
272 | - throw new Exception('Not installed'); |
|
273 | - } else { |
|
274 | - $url = OC::$WEBROOT . '/index.php'; |
|
275 | - header('Location: ' . $url); |
|
276 | - } |
|
277 | - exit(); |
|
278 | - } |
|
279 | - } |
|
280 | - |
|
281 | - public static function checkMaintenanceMode() { |
|
282 | - // Allow ajax update script to execute without being stopped |
|
283 | - if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') { |
|
284 | - // send http status 503 |
|
285 | - header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
286 | - header('Status: 503 Service Temporarily Unavailable'); |
|
287 | - header('Retry-After: 120'); |
|
288 | - |
|
289 | - // render error page |
|
290 | - $template = new OC_Template('', 'update.user', 'guest'); |
|
291 | - OC_Util::addScript('maintenance-check'); |
|
292 | - OC_Util::addStyle('core', 'guest'); |
|
293 | - $template->printPage(); |
|
294 | - die(); |
|
295 | - } |
|
296 | - } |
|
297 | - |
|
298 | - /** |
|
299 | - * Prints the upgrade page |
|
300 | - * |
|
301 | - * @param \OC\SystemConfig $systemConfig |
|
302 | - */ |
|
303 | - private static function printUpgradePage(\OC\SystemConfig $systemConfig) { |
|
304 | - $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false); |
|
305 | - $tooBig = false; |
|
306 | - if (!$disableWebUpdater) { |
|
307 | - $apps = \OC::$server->getAppManager(); |
|
308 | - if ($apps->isInstalled('user_ldap')) { |
|
309 | - $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
|
310 | - |
|
311 | - $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') |
|
312 | - ->from('ldap_user_mapping') |
|
313 | - ->execute(); |
|
314 | - $row = $result->fetch(); |
|
315 | - $result->closeCursor(); |
|
316 | - |
|
317 | - $tooBig = ($row['user_count'] > 50); |
|
318 | - } |
|
319 | - if (!$tooBig && $apps->isInstalled('user_saml')) { |
|
320 | - $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
|
321 | - |
|
322 | - $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') |
|
323 | - ->from('user_saml_users') |
|
324 | - ->execute(); |
|
325 | - $row = $result->fetch(); |
|
326 | - $result->closeCursor(); |
|
327 | - |
|
328 | - $tooBig = ($row['user_count'] > 50); |
|
329 | - } |
|
330 | - if (!$tooBig) { |
|
331 | - // count users |
|
332 | - $stats = \OC::$server->getUserManager()->countUsers(); |
|
333 | - $totalUsers = array_sum($stats); |
|
334 | - $tooBig = ($totalUsers > 50); |
|
335 | - } |
|
336 | - } |
|
337 | - $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) && |
|
338 | - $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis'; |
|
339 | - |
|
340 | - if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) { |
|
341 | - // send http status 503 |
|
342 | - header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
343 | - header('Status: 503 Service Temporarily Unavailable'); |
|
344 | - header('Retry-After: 120'); |
|
345 | - |
|
346 | - // render error page |
|
347 | - $template = new OC_Template('', 'update.use-cli', 'guest'); |
|
348 | - $template->assign('productName', 'nextcloud'); // for now |
|
349 | - $template->assign('version', OC_Util::getVersionString()); |
|
350 | - $template->assign('tooBig', $tooBig); |
|
351 | - |
|
352 | - $template->printPage(); |
|
353 | - die(); |
|
354 | - } |
|
355 | - |
|
356 | - // check whether this is a core update or apps update |
|
357 | - $installedVersion = $systemConfig->getValue('version', '0.0.0'); |
|
358 | - $currentVersion = implode('.', \OCP\Util::getVersion()); |
|
359 | - |
|
360 | - // if not a core upgrade, then it's apps upgrade |
|
361 | - $isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '='); |
|
362 | - |
|
363 | - $oldTheme = $systemConfig->getValue('theme'); |
|
364 | - $systemConfig->setValue('theme', ''); |
|
365 | - OC_Util::addScript('config'); // needed for web root |
|
366 | - OC_Util::addScript('update'); |
|
367 | - |
|
368 | - /** @var \OC\App\AppManager $appManager */ |
|
369 | - $appManager = \OC::$server->getAppManager(); |
|
370 | - |
|
371 | - $tmpl = new OC_Template('', 'update.admin', 'guest'); |
|
372 | - $tmpl->assign('version', OC_Util::getVersionString()); |
|
373 | - $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade); |
|
374 | - |
|
375 | - // get third party apps |
|
376 | - $ocVersion = \OCP\Util::getVersion(); |
|
377 | - $ocVersion = implode('.', $ocVersion); |
|
378 | - $incompatibleApps = $appManager->getIncompatibleApps($ocVersion); |
|
379 | - $incompatibleShippedApps = []; |
|
380 | - foreach ($incompatibleApps as $appInfo) { |
|
381 | - if ($appManager->isShipped($appInfo['id'])) { |
|
382 | - $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')'; |
|
383 | - } |
|
384 | - } |
|
385 | - |
|
386 | - if (!empty($incompatibleShippedApps)) { |
|
387 | - $l = \OC::$server->getL10N('core'); |
|
388 | - $hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]); |
|
389 | - throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint); |
|
390 | - } |
|
391 | - |
|
392 | - $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion)); |
|
393 | - $tmpl->assign('incompatibleAppsList', $incompatibleApps); |
|
394 | - $tmpl->assign('productName', 'Nextcloud'); // for now |
|
395 | - $tmpl->assign('oldTheme', $oldTheme); |
|
396 | - $tmpl->printPage(); |
|
397 | - } |
|
398 | - |
|
399 | - public static function initSession() { |
|
400 | - if(self::$server->getRequest()->getServerProtocol() === 'https') { |
|
401 | - ini_set('session.cookie_secure', true); |
|
402 | - } |
|
403 | - |
|
404 | - // prevents javascript from accessing php session cookies |
|
405 | - ini_set('session.cookie_httponly', 'true'); |
|
406 | - |
|
407 | - // set the cookie path to the Nextcloud directory |
|
408 | - $cookie_path = OC::$WEBROOT ? : '/'; |
|
409 | - ini_set('session.cookie_path', $cookie_path); |
|
410 | - |
|
411 | - // Let the session name be changed in the initSession Hook |
|
412 | - $sessionName = OC_Util::getInstanceId(); |
|
413 | - |
|
414 | - try { |
|
415 | - // Allow session apps to create a custom session object |
|
416 | - $useCustomSession = false; |
|
417 | - $session = self::$server->getSession(); |
|
418 | - OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession)); |
|
419 | - if (!$useCustomSession) { |
|
420 | - // set the session name to the instance id - which is unique |
|
421 | - $session = new \OC\Session\Internal($sessionName); |
|
422 | - } |
|
423 | - |
|
424 | - $cryptoWrapper = \OC::$server->getSessionCryptoWrapper(); |
|
425 | - $session = $cryptoWrapper->wrapSession($session); |
|
426 | - self::$server->setSession($session); |
|
427 | - |
|
428 | - // if session can't be started break with http 500 error |
|
429 | - } catch (Exception $e) { |
|
430 | - \OC::$server->getLogger()->logException($e, ['app' => 'base']); |
|
431 | - //show the user a detailed error page |
|
432 | - OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR); |
|
433 | - OC_Template::printExceptionErrorPage($e); |
|
434 | - die(); |
|
435 | - } |
|
436 | - |
|
437 | - $sessionLifeTime = self::getSessionLifeTime(); |
|
438 | - |
|
439 | - // session timeout |
|
440 | - if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) { |
|
441 | - if (isset($_COOKIE[session_name()])) { |
|
442 | - setcookie(session_name(), null, -1, self::$WEBROOT ? : '/'); |
|
443 | - } |
|
444 | - \OC::$server->getUserSession()->logout(); |
|
445 | - } |
|
446 | - |
|
447 | - $session->set('LAST_ACTIVITY', time()); |
|
448 | - } |
|
449 | - |
|
450 | - /** |
|
451 | - * @return string |
|
452 | - */ |
|
453 | - private static function getSessionLifeTime() { |
|
454 | - return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24); |
|
455 | - } |
|
456 | - |
|
457 | - public static function loadAppClassPaths() { |
|
458 | - foreach (OC_App::getEnabledApps() as $app) { |
|
459 | - $appPath = OC_App::getAppPath($app); |
|
460 | - if ($appPath === false) { |
|
461 | - continue; |
|
462 | - } |
|
463 | - |
|
464 | - $file = $appPath . '/appinfo/classpath.php'; |
|
465 | - if (file_exists($file)) { |
|
466 | - require_once $file; |
|
467 | - } |
|
468 | - } |
|
469 | - } |
|
470 | - |
|
471 | - /** |
|
472 | - * Try to set some values to the required Nextcloud default |
|
473 | - */ |
|
474 | - public static function setRequiredIniValues() { |
|
475 | - @ini_set('default_charset', 'UTF-8'); |
|
476 | - @ini_set('gd.jpeg_ignore_warning', '1'); |
|
477 | - } |
|
478 | - |
|
479 | - /** |
|
480 | - * Send the same site cookies |
|
481 | - */ |
|
482 | - private static function sendSameSiteCookies() { |
|
483 | - $cookieParams = session_get_cookie_params(); |
|
484 | - $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : ''; |
|
485 | - $policies = [ |
|
486 | - 'lax', |
|
487 | - 'strict', |
|
488 | - ]; |
|
489 | - |
|
490 | - // Append __Host to the cookie if it meets the requirements |
|
491 | - $cookiePrefix = ''; |
|
492 | - if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { |
|
493 | - $cookiePrefix = '__Host-'; |
|
494 | - } |
|
495 | - |
|
496 | - foreach($policies as $policy) { |
|
497 | - header( |
|
498 | - sprintf( |
|
499 | - 'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s', |
|
500 | - $cookiePrefix, |
|
501 | - $policy, |
|
502 | - $cookieParams['path'], |
|
503 | - $policy |
|
504 | - ), |
|
505 | - false |
|
506 | - ); |
|
507 | - } |
|
508 | - } |
|
509 | - |
|
510 | - /** |
|
511 | - * Same Site cookie to further mitigate CSRF attacks. This cookie has to |
|
512 | - * be set in every request if cookies are sent to add a second level of |
|
513 | - * defense against CSRF. |
|
514 | - * |
|
515 | - * If the cookie is not sent this will set the cookie and reload the page. |
|
516 | - * We use an additional cookie since we want to protect logout CSRF and |
|
517 | - * also we can't directly interfere with PHP's session mechanism. |
|
518 | - */ |
|
519 | - private static function performSameSiteCookieProtection() { |
|
520 | - $request = \OC::$server->getRequest(); |
|
521 | - |
|
522 | - // Some user agents are notorious and don't really properly follow HTTP |
|
523 | - // specifications. For those, have an automated opt-out. Since the protection |
|
524 | - // for remote.php is applied in base.php as starting point we need to opt out |
|
525 | - // here. |
|
526 | - $incompatibleUserAgents = [ |
|
527 | - // OS X Finder |
|
528 | - '/^WebDAVFS/', |
|
529 | - '/^Microsoft-WebDAV-MiniRedir/', |
|
530 | - ]; |
|
531 | - if($request->isUserAgent($incompatibleUserAgents)) { |
|
532 | - return; |
|
533 | - } |
|
534 | - |
|
535 | - if(count($_COOKIE) > 0) { |
|
536 | - $requestUri = $request->getScriptName(); |
|
537 | - $processingScript = explode('/', $requestUri); |
|
538 | - $processingScript = $processingScript[count($processingScript)-1]; |
|
539 | - |
|
540 | - // index.php routes are handled in the middleware |
|
541 | - if($processingScript === 'index.php') { |
|
542 | - return; |
|
543 | - } |
|
544 | - |
|
545 | - // All other endpoints require the lax and the strict cookie |
|
546 | - if(!$request->passesStrictCookieCheck()) { |
|
547 | - self::sendSameSiteCookies(); |
|
548 | - // Debug mode gets access to the resources without strict cookie |
|
549 | - // due to the fact that the SabreDAV browser also lives there. |
|
550 | - if(!\OC::$server->getConfig()->getSystemValue('debug', false)) { |
|
551 | - http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE); |
|
552 | - exit(); |
|
553 | - } |
|
554 | - } |
|
555 | - } elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) { |
|
556 | - self::sendSameSiteCookies(); |
|
557 | - } |
|
558 | - } |
|
559 | - |
|
560 | - public static function init() { |
|
561 | - // calculate the root directories |
|
562 | - OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4)); |
|
563 | - |
|
564 | - // register autoloader |
|
565 | - $loaderStart = microtime(true); |
|
566 | - require_once __DIR__ . '/autoloader.php'; |
|
567 | - self::$loader = new \OC\Autoloader([ |
|
568 | - OC::$SERVERROOT . '/lib/private/legacy', |
|
569 | - ]); |
|
570 | - if (defined('PHPUNIT_RUN')) { |
|
571 | - self::$loader->addValidRoot(OC::$SERVERROOT . '/tests'); |
|
572 | - } |
|
573 | - spl_autoload_register(array(self::$loader, 'load')); |
|
574 | - $loaderEnd = microtime(true); |
|
575 | - |
|
576 | - self::$CLI = (php_sapi_name() == 'cli'); |
|
577 | - |
|
578 | - // Add default composer PSR-4 autoloader |
|
579 | - self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php'; |
|
580 | - |
|
581 | - try { |
|
582 | - self::initPaths(); |
|
583 | - // setup 3rdparty autoloader |
|
584 | - $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php'; |
|
585 | - if (!file_exists($vendorAutoLoad)) { |
|
586 | - throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".'); |
|
587 | - } |
|
588 | - require_once $vendorAutoLoad; |
|
589 | - |
|
590 | - } catch (\RuntimeException $e) { |
|
591 | - if (!self::$CLI) { |
|
592 | - $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']); |
|
593 | - $protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1'; |
|
594 | - header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE); |
|
595 | - } |
|
596 | - // we can't use the template error page here, because this needs the |
|
597 | - // DI container which isn't available yet |
|
598 | - print($e->getMessage()); |
|
599 | - exit(); |
|
600 | - } |
|
601 | - |
|
602 | - // setup the basic server |
|
603 | - self::$server = new \OC\Server(\OC::$WEBROOT, self::$config); |
|
604 | - \OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd); |
|
605 | - \OC::$server->getEventLogger()->start('boot', 'Initialize'); |
|
606 | - |
|
607 | - // Don't display errors and log them |
|
608 | - error_reporting(E_ALL | E_STRICT); |
|
609 | - @ini_set('display_errors', '0'); |
|
610 | - @ini_set('log_errors', '1'); |
|
611 | - |
|
612 | - if(!date_default_timezone_set('UTC')) { |
|
613 | - throw new \RuntimeException('Could not set timezone to UTC'); |
|
614 | - } |
|
615 | - |
|
616 | - //try to configure php to enable big file uploads. |
|
617 | - //this doesn´t work always depending on the webserver and php configuration. |
|
618 | - //Let´s try to overwrite some defaults anyway |
|
619 | - |
|
620 | - //try to set the maximum execution time to 60min |
|
621 | - if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { |
|
622 | - @set_time_limit(3600); |
|
623 | - } |
|
624 | - @ini_set('max_execution_time', '3600'); |
|
625 | - @ini_set('max_input_time', '3600'); |
|
626 | - |
|
627 | - //try to set the maximum filesize to 10G |
|
628 | - @ini_set('upload_max_filesize', '10G'); |
|
629 | - @ini_set('post_max_size', '10G'); |
|
630 | - @ini_set('file_uploads', '50'); |
|
631 | - |
|
632 | - self::setRequiredIniValues(); |
|
633 | - self::handleAuthHeaders(); |
|
634 | - self::registerAutoloaderCache(); |
|
635 | - |
|
636 | - // initialize intl fallback is necessary |
|
637 | - \Patchwork\Utf8\Bootup::initIntl(); |
|
638 | - OC_Util::isSetLocaleWorking(); |
|
639 | - |
|
640 | - if (!defined('PHPUNIT_RUN')) { |
|
641 | - OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger()); |
|
642 | - $debug = \OC::$server->getConfig()->getSystemValue('debug', false); |
|
643 | - OC\Log\ErrorHandler::register($debug); |
|
644 | - } |
|
645 | - |
|
646 | - \OC::$server->getEventLogger()->start('init_session', 'Initialize session'); |
|
647 | - OC_App::loadApps(array('session')); |
|
648 | - if (!self::$CLI) { |
|
649 | - self::initSession(); |
|
650 | - } |
|
651 | - \OC::$server->getEventLogger()->end('init_session'); |
|
652 | - self::checkConfig(); |
|
653 | - self::checkInstalled(); |
|
654 | - |
|
655 | - OC_Response::addSecurityHeaders(); |
|
656 | - |
|
657 | - self::performSameSiteCookieProtection(); |
|
658 | - |
|
659 | - if (!defined('OC_CONSOLE')) { |
|
660 | - $errors = OC_Util::checkServer(\OC::$server->getSystemConfig()); |
|
661 | - if (count($errors) > 0) { |
|
662 | - if (self::$CLI) { |
|
663 | - // Convert l10n string into regular string for usage in database |
|
664 | - $staticErrors = []; |
|
665 | - foreach ($errors as $error) { |
|
666 | - echo $error['error'] . "\n"; |
|
667 | - echo $error['hint'] . "\n\n"; |
|
668 | - $staticErrors[] = [ |
|
669 | - 'error' => (string)$error['error'], |
|
670 | - 'hint' => (string)$error['hint'], |
|
671 | - ]; |
|
672 | - } |
|
673 | - |
|
674 | - try { |
|
675 | - \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors)); |
|
676 | - } catch (\Exception $e) { |
|
677 | - echo('Writing to database failed'); |
|
678 | - } |
|
679 | - exit(1); |
|
680 | - } else { |
|
681 | - OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE); |
|
682 | - OC_Util::addStyle('guest'); |
|
683 | - OC_Template::printGuestPage('', 'error', array('errors' => $errors)); |
|
684 | - exit; |
|
685 | - } |
|
686 | - } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) { |
|
687 | - \OC::$server->getConfig()->deleteAppValue('core', 'cronErrors'); |
|
688 | - } |
|
689 | - } |
|
690 | - //try to set the session lifetime |
|
691 | - $sessionLifeTime = self::getSessionLifeTime(); |
|
692 | - @ini_set('gc_maxlifetime', (string)$sessionLifeTime); |
|
693 | - |
|
694 | - $systemConfig = \OC::$server->getSystemConfig(); |
|
695 | - |
|
696 | - // User and Groups |
|
697 | - if (!$systemConfig->getValue("installed", false)) { |
|
698 | - self::$server->getSession()->set('user_id', ''); |
|
699 | - } |
|
700 | - |
|
701 | - OC_User::useBackend(new \OC\User\Database()); |
|
702 | - \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database()); |
|
703 | - |
|
704 | - // Subscribe to the hook |
|
705 | - \OCP\Util::connectHook( |
|
706 | - '\OCA\Files_Sharing\API\Server2Server', |
|
707 | - 'preLoginNameUsedAsUserName', |
|
708 | - '\OC\User\Database', |
|
709 | - 'preLoginNameUsedAsUserName' |
|
710 | - ); |
|
711 | - |
|
712 | - //setup extra user backends |
|
713 | - if (!\OCP\Util::needUpgrade()) { |
|
714 | - OC_User::setupBackends(); |
|
715 | - } else { |
|
716 | - // Run upgrades in incognito mode |
|
717 | - OC_User::setIncognitoMode(true); |
|
718 | - } |
|
719 | - |
|
720 | - self::registerCleanupHooks(); |
|
721 | - self::registerFilesystemHooks(); |
|
722 | - self::registerShareHooks(); |
|
723 | - self::registerEncryptionWrapper(); |
|
724 | - self::registerEncryptionHooks(); |
|
725 | - self::registerAccountHooks(); |
|
726 | - |
|
727 | - // Make sure that the application class is not loaded before the database is setup |
|
728 | - if ($systemConfig->getValue("installed", false)) { |
|
729 | - $settings = new \OC\Settings\Application(); |
|
730 | - $settings->register(); |
|
731 | - } |
|
732 | - |
|
733 | - //make sure temporary files are cleaned up |
|
734 | - $tmpManager = \OC::$server->getTempManager(); |
|
735 | - register_shutdown_function(array($tmpManager, 'clean')); |
|
736 | - $lockProvider = \OC::$server->getLockingProvider(); |
|
737 | - register_shutdown_function(array($lockProvider, 'releaseAll')); |
|
738 | - |
|
739 | - // Check whether the sample configuration has been copied |
|
740 | - if($systemConfig->getValue('copied_sample_config', false)) { |
|
741 | - $l = \OC::$server->getL10N('lib'); |
|
742 | - header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
743 | - header('Status: 503 Service Temporarily Unavailable'); |
|
744 | - OC_Template::printErrorPage( |
|
745 | - $l->t('Sample configuration detected'), |
|
746 | - $l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php') |
|
747 | - ); |
|
748 | - return; |
|
749 | - } |
|
750 | - |
|
751 | - $request = \OC::$server->getRequest(); |
|
752 | - $host = $request->getInsecureServerHost(); |
|
753 | - /** |
|
754 | - * if the host passed in headers isn't trusted |
|
755 | - * FIXME: Should not be in here at all :see_no_evil: |
|
756 | - */ |
|
757 | - if (!OC::$CLI |
|
758 | - // overwritehost is always trusted, workaround to not have to make |
|
759 | - // \OC\AppFramework\Http\Request::getOverwriteHost public |
|
760 | - && self::$server->getConfig()->getSystemValue('overwritehost') === '' |
|
761 | - && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host) |
|
762 | - && self::$server->getConfig()->getSystemValue('installed', false) |
|
763 | - ) { |
|
764 | - // Allow access to CSS resources |
|
765 | - $isScssRequest = false; |
|
766 | - if(strpos($request->getPathInfo(), '/css/') === 0) { |
|
767 | - $isScssRequest = true; |
|
768 | - } |
|
769 | - |
|
770 | - if(substr($request->getRequestUri(), -11) === '/status.php') { |
|
771 | - OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST); |
|
772 | - header('Status: 400 Bad Request'); |
|
773 | - header('Content-Type: application/json'); |
|
774 | - echo '{"error": "Trusted domain error.", "code": 15}'; |
|
775 | - exit(); |
|
776 | - } |
|
777 | - |
|
778 | - if (!$isScssRequest) { |
|
779 | - OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST); |
|
780 | - header('Status: 400 Bad Request'); |
|
781 | - |
|
782 | - \OC::$server->getLogger()->info( |
|
783 | - 'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.', |
|
784 | - [ |
|
785 | - 'app' => 'core', |
|
786 | - 'remoteAddress' => $request->getRemoteAddress(), |
|
787 | - 'host' => $host, |
|
788 | - ] |
|
789 | - ); |
|
790 | - |
|
791 | - $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); |
|
792 | - $tmpl->assign('docUrl', \OC::$server->getURLGenerator()->linkToDocs('admin-trusted-domains')); |
|
793 | - $tmpl->printPage(); |
|
794 | - |
|
795 | - exit(); |
|
796 | - } |
|
797 | - } |
|
798 | - \OC::$server->getEventLogger()->end('boot'); |
|
799 | - } |
|
800 | - |
|
801 | - /** |
|
802 | - * register hooks for the cleanup of cache and bruteforce protection |
|
803 | - */ |
|
804 | - public static function registerCleanupHooks() { |
|
805 | - //don't try to do this before we are properly setup |
|
806 | - if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) { |
|
807 | - |
|
808 | - // NOTE: This will be replaced to use OCP |
|
809 | - $userSession = self::$server->getUserSession(); |
|
810 | - $userSession->listen('\OC\User', 'postLogin', function () use ($userSession) { |
|
811 | - if (!defined('PHPUNIT_RUN')) { |
|
812 | - // reset brute force delay for this IP address and username |
|
813 | - $uid = \OC::$server->getUserSession()->getUser()->getUID(); |
|
814 | - $request = \OC::$server->getRequest(); |
|
815 | - $throttler = \OC::$server->getBruteForceThrottler(); |
|
816 | - $throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]); |
|
817 | - } |
|
818 | - |
|
819 | - try { |
|
820 | - $cache = new \OC\Cache\File(); |
|
821 | - $cache->gc(); |
|
822 | - } catch (\OC\ServerNotAvailableException $e) { |
|
823 | - // not a GC exception, pass it on |
|
824 | - throw $e; |
|
825 | - } catch (\OC\ForbiddenException $e) { |
|
826 | - // filesystem blocked for this request, ignore |
|
827 | - } catch (\Exception $e) { |
|
828 | - // a GC exception should not prevent users from using OC, |
|
829 | - // so log the exception |
|
830 | - \OC::$server->getLogger()->logException($e, [ |
|
831 | - 'message' => 'Exception when running cache gc.', |
|
832 | - 'level' => ILogger::WARN, |
|
833 | - 'app' => 'core', |
|
834 | - ]); |
|
835 | - } |
|
836 | - }); |
|
837 | - } |
|
838 | - } |
|
839 | - |
|
840 | - private static function registerEncryptionWrapper() { |
|
841 | - $manager = self::$server->getEncryptionManager(); |
|
842 | - \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage'); |
|
843 | - } |
|
844 | - |
|
845 | - private static function registerEncryptionHooks() { |
|
846 | - $enabled = self::$server->getEncryptionManager()->isEnabled(); |
|
847 | - if ($enabled) { |
|
848 | - \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared'); |
|
849 | - \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared'); |
|
850 | - \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename'); |
|
851 | - \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore'); |
|
852 | - } |
|
853 | - } |
|
854 | - |
|
855 | - private static function registerAccountHooks() { |
|
856 | - $hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger()); |
|
857 | - \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook'); |
|
858 | - } |
|
859 | - |
|
860 | - /** |
|
861 | - * register hooks for the filesystem |
|
862 | - */ |
|
863 | - public static function registerFilesystemHooks() { |
|
864 | - // Check for blacklisted files |
|
865 | - OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted'); |
|
866 | - OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted'); |
|
867 | - } |
|
868 | - |
|
869 | - /** |
|
870 | - * register hooks for sharing |
|
871 | - */ |
|
872 | - public static function registerShareHooks() { |
|
873 | - if (\OC::$server->getSystemConfig()->getValue('installed')) { |
|
874 | - OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser'); |
|
875 | - OC_Hook::connect('OC_User', 'post_removeFromGroup', Hooks::class, 'post_removeFromGroup'); |
|
876 | - OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup'); |
|
877 | - } |
|
878 | - } |
|
879 | - |
|
880 | - protected static function registerAutoloaderCache() { |
|
881 | - // The class loader takes an optional low-latency cache, which MUST be |
|
882 | - // namespaced. The instanceid is used for namespacing, but might be |
|
883 | - // unavailable at this point. Furthermore, it might not be possible to |
|
884 | - // generate an instanceid via \OC_Util::getInstanceId() because the |
|
885 | - // config file may not be writable. As such, we only register a class |
|
886 | - // loader cache if instanceid is available without trying to create one. |
|
887 | - $instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null); |
|
888 | - if ($instanceId) { |
|
889 | - try { |
|
890 | - $memcacheFactory = \OC::$server->getMemCacheFactory(); |
|
891 | - self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader')); |
|
892 | - } catch (\Exception $ex) { |
|
893 | - } |
|
894 | - } |
|
895 | - } |
|
896 | - |
|
897 | - /** |
|
898 | - * Handle the request |
|
899 | - */ |
|
900 | - public static function handleRequest() { |
|
901 | - |
|
902 | - \OC::$server->getEventLogger()->start('handle_request', 'Handle request'); |
|
903 | - $systemConfig = \OC::$server->getSystemConfig(); |
|
904 | - // load all the classpaths from the enabled apps so they are available |
|
905 | - // in the routing files of each app |
|
906 | - OC::loadAppClassPaths(); |
|
907 | - |
|
908 | - // Check if Nextcloud is installed or in maintenance (update) mode |
|
909 | - if (!$systemConfig->getValue('installed', false)) { |
|
910 | - \OC::$server->getSession()->clear(); |
|
911 | - $setupHelper = new OC\Setup( |
|
912 | - $systemConfig, |
|
913 | - \OC::$server->getIniWrapper(), |
|
914 | - \OC::$server->getL10N('lib'), |
|
915 | - \OC::$server->query(\OCP\Defaults::class), |
|
916 | - \OC::$server->getLogger(), |
|
917 | - \OC::$server->getSecureRandom(), |
|
918 | - \OC::$server->query(\OC\Installer::class) |
|
919 | - ); |
|
920 | - $controller = new OC\Core\Controller\SetupController($setupHelper); |
|
921 | - $controller->run($_POST); |
|
922 | - exit(); |
|
923 | - } |
|
924 | - |
|
925 | - $request = \OC::$server->getRequest(); |
|
926 | - $requestPath = $request->getRawPathInfo(); |
|
927 | - if ($requestPath === '/heartbeat') { |
|
928 | - return; |
|
929 | - } |
|
930 | - if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade |
|
931 | - self::checkMaintenanceMode(); |
|
932 | - |
|
933 | - if (\OCP\Util::needUpgrade()) { |
|
934 | - if (function_exists('opcache_reset')) { |
|
935 | - opcache_reset(); |
|
936 | - } |
|
937 | - if (!$systemConfig->getValue('maintenance', false)) { |
|
938 | - self::printUpgradePage($systemConfig); |
|
939 | - exit(); |
|
940 | - } |
|
941 | - } |
|
942 | - } |
|
943 | - |
|
944 | - // emergency app disabling |
|
945 | - if ($requestPath === '/disableapp' |
|
946 | - && $request->getMethod() === 'POST' |
|
947 | - && ((array)$request->getParam('appid')) !== '' |
|
948 | - ) { |
|
949 | - \OC_JSON::callCheck(); |
|
950 | - \OC_JSON::checkAdminUser(); |
|
951 | - $appIds = (array)$request->getParam('appid'); |
|
952 | - foreach($appIds as $appId) { |
|
953 | - $appId = \OC_App::cleanAppId($appId); |
|
954 | - \OC::$server->getAppManager()->disableApp($appId); |
|
955 | - } |
|
956 | - \OC_JSON::success(); |
|
957 | - exit(); |
|
958 | - } |
|
959 | - |
|
960 | - // Always load authentication apps |
|
961 | - OC_App::loadApps(['authentication']); |
|
962 | - |
|
963 | - // Load minimum set of apps |
|
964 | - if (!\OCP\Util::needUpgrade() |
|
965 | - && !$systemConfig->getValue('maintenance', false)) { |
|
966 | - // For logged-in users: Load everything |
|
967 | - if(\OC::$server->getUserSession()->isLoggedIn()) { |
|
968 | - OC_App::loadApps(); |
|
969 | - } else { |
|
970 | - // For guests: Load only filesystem and logging |
|
971 | - OC_App::loadApps(array('filesystem', 'logging')); |
|
972 | - self::handleLogin($request); |
|
973 | - } |
|
974 | - } |
|
975 | - |
|
976 | - if (!self::$CLI) { |
|
977 | - try { |
|
978 | - if (!$systemConfig->getValue('maintenance', false) && !\OCP\Util::needUpgrade()) { |
|
979 | - OC_App::loadApps(array('filesystem', 'logging')); |
|
980 | - OC_App::loadApps(); |
|
981 | - } |
|
982 | - OC_Util::setupFS(); |
|
983 | - OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo()); |
|
984 | - return; |
|
985 | - } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { |
|
986 | - //header('HTTP/1.0 404 Not Found'); |
|
987 | - } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { |
|
988 | - OC_Response::setStatus(405); |
|
989 | - return; |
|
990 | - } |
|
991 | - } |
|
992 | - |
|
993 | - // Handle WebDAV |
|
994 | - if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { |
|
995 | - // not allowed any more to prevent people |
|
996 | - // mounting this root directly. |
|
997 | - // Users need to mount remote.php/webdav instead. |
|
998 | - header('HTTP/1.1 405 Method Not Allowed'); |
|
999 | - header('Status: 405 Method Not Allowed'); |
|
1000 | - return; |
|
1001 | - } |
|
1002 | - |
|
1003 | - // Someone is logged in |
|
1004 | - if (\OC::$server->getUserSession()->isLoggedIn()) { |
|
1005 | - OC_App::loadApps(); |
|
1006 | - OC_User::setupBackends(); |
|
1007 | - OC_Util::setupFS(); |
|
1008 | - // FIXME |
|
1009 | - // Redirect to default application |
|
1010 | - OC_Util::redirectToDefaultPage(); |
|
1011 | - } else { |
|
1012 | - // Not handled and not logged in |
|
1013 | - header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm')); |
|
1014 | - } |
|
1015 | - } |
|
1016 | - |
|
1017 | - /** |
|
1018 | - * Check login: apache auth, auth token, basic auth |
|
1019 | - * |
|
1020 | - * @param OCP\IRequest $request |
|
1021 | - * @return boolean |
|
1022 | - */ |
|
1023 | - static function handleLogin(OCP\IRequest $request) { |
|
1024 | - $userSession = self::$server->getUserSession(); |
|
1025 | - if (OC_User::handleApacheAuth()) { |
|
1026 | - return true; |
|
1027 | - } |
|
1028 | - if ($userSession->tryTokenLogin($request)) { |
|
1029 | - return true; |
|
1030 | - } |
|
1031 | - if (isset($_COOKIE['nc_username']) |
|
1032 | - && isset($_COOKIE['nc_token']) |
|
1033 | - && isset($_COOKIE['nc_session_id']) |
|
1034 | - && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) { |
|
1035 | - return true; |
|
1036 | - } |
|
1037 | - if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { |
|
1038 | - return true; |
|
1039 | - } |
|
1040 | - return false; |
|
1041 | - } |
|
1042 | - |
|
1043 | - protected static function handleAuthHeaders() { |
|
1044 | - //copy http auth headers for apache+php-fcgid work around |
|
1045 | - if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) { |
|
1046 | - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION']; |
|
1047 | - } |
|
1048 | - |
|
1049 | - // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary. |
|
1050 | - $vars = array( |
|
1051 | - 'HTTP_AUTHORIZATION', // apache+php-cgi work around |
|
1052 | - 'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative |
|
1053 | - ); |
|
1054 | - foreach ($vars as $var) { |
|
1055 | - if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) { |
|
1056 | - list($name, $password) = explode(':', base64_decode($matches[1]), 2); |
|
1057 | - $_SERVER['PHP_AUTH_USER'] = $name; |
|
1058 | - $_SERVER['PHP_AUTH_PW'] = $password; |
|
1059 | - break; |
|
1060 | - } |
|
1061 | - } |
|
1062 | - } |
|
71 | + /** |
|
72 | + * Associative array for autoloading. classname => filename |
|
73 | + */ |
|
74 | + public static $CLASSPATH = array(); |
|
75 | + /** |
|
76 | + * The installation path for Nextcloud on the server (e.g. /srv/http/nextcloud) |
|
77 | + */ |
|
78 | + public static $SERVERROOT = ''; |
|
79 | + /** |
|
80 | + * the current request path relative to the Nextcloud root (e.g. files/index.php) |
|
81 | + */ |
|
82 | + private static $SUBURI = ''; |
|
83 | + /** |
|
84 | + * the Nextcloud root path for http requests (e.g. nextcloud/) |
|
85 | + */ |
|
86 | + public static $WEBROOT = ''; |
|
87 | + /** |
|
88 | + * The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and |
|
89 | + * web path in 'url' |
|
90 | + */ |
|
91 | + public static $APPSROOTS = array(); |
|
92 | + |
|
93 | + /** |
|
94 | + * @var string |
|
95 | + */ |
|
96 | + public static $configDir; |
|
97 | + |
|
98 | + /** |
|
99 | + * requested app |
|
100 | + */ |
|
101 | + public static $REQUESTEDAPP = ''; |
|
102 | + |
|
103 | + /** |
|
104 | + * check if Nextcloud runs in cli mode |
|
105 | + */ |
|
106 | + public static $CLI = false; |
|
107 | + |
|
108 | + /** |
|
109 | + * @var \OC\Autoloader $loader |
|
110 | + */ |
|
111 | + public static $loader = null; |
|
112 | + |
|
113 | + /** @var \Composer\Autoload\ClassLoader $composerAutoloader */ |
|
114 | + public static $composerAutoloader = null; |
|
115 | + |
|
116 | + /** |
|
117 | + * @var \OC\Server |
|
118 | + */ |
|
119 | + public static $server = null; |
|
120 | + |
|
121 | + /** |
|
122 | + * @var \OC\Config |
|
123 | + */ |
|
124 | + private static $config = null; |
|
125 | + |
|
126 | + /** |
|
127 | + * @throws \RuntimeException when the 3rdparty directory is missing or |
|
128 | + * the app path list is empty or contains an invalid path |
|
129 | + */ |
|
130 | + public static function initPaths() { |
|
131 | + if(defined('PHPUNIT_CONFIG_DIR')) { |
|
132 | + self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/'; |
|
133 | + } elseif(defined('PHPUNIT_RUN') and PHPUNIT_RUN and is_dir(OC::$SERVERROOT . '/tests/config/')) { |
|
134 | + self::$configDir = OC::$SERVERROOT . '/tests/config/'; |
|
135 | + } elseif($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { |
|
136 | + self::$configDir = rtrim($dir, '/') . '/'; |
|
137 | + } else { |
|
138 | + self::$configDir = OC::$SERVERROOT . '/config/'; |
|
139 | + } |
|
140 | + self::$config = new \OC\Config(self::$configDir); |
|
141 | + |
|
142 | + OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT))); |
|
143 | + /** |
|
144 | + * FIXME: The following lines are required because we can't yet instantiate |
|
145 | + * \OC::$server->getRequest() since \OC::$server does not yet exist. |
|
146 | + */ |
|
147 | + $params = [ |
|
148 | + 'server' => [ |
|
149 | + 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], |
|
150 | + 'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'], |
|
151 | + ], |
|
152 | + ]; |
|
153 | + $fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig(self::$config))); |
|
154 | + $scriptName = $fakeRequest->getScriptName(); |
|
155 | + if (substr($scriptName, -1) == '/') { |
|
156 | + $scriptName .= 'index.php'; |
|
157 | + //make sure suburi follows the same rules as scriptName |
|
158 | + if (substr(OC::$SUBURI, -9) != 'index.php') { |
|
159 | + if (substr(OC::$SUBURI, -1) != '/') { |
|
160 | + OC::$SUBURI = OC::$SUBURI . '/'; |
|
161 | + } |
|
162 | + OC::$SUBURI = OC::$SUBURI . 'index.php'; |
|
163 | + } |
|
164 | + } |
|
165 | + |
|
166 | + |
|
167 | + if (OC::$CLI) { |
|
168 | + OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); |
|
169 | + } else { |
|
170 | + if (substr($scriptName, 0 - strlen(OC::$SUBURI)) === OC::$SUBURI) { |
|
171 | + OC::$WEBROOT = substr($scriptName, 0, 0 - strlen(OC::$SUBURI)); |
|
172 | + |
|
173 | + if (OC::$WEBROOT != '' && OC::$WEBROOT[0] !== '/') { |
|
174 | + OC::$WEBROOT = '/' . OC::$WEBROOT; |
|
175 | + } |
|
176 | + } else { |
|
177 | + // The scriptName is not ending with OC::$SUBURI |
|
178 | + // This most likely means that we are calling from CLI. |
|
179 | + // However some cron jobs still need to generate |
|
180 | + // a web URL, so we use overwritewebroot as a fallback. |
|
181 | + OC::$WEBROOT = self::$config->getValue('overwritewebroot', ''); |
|
182 | + } |
|
183 | + |
|
184 | + // Resolve /nextcloud to /nextcloud/ to ensure to always have a trailing |
|
185 | + // slash which is required by URL generation. |
|
186 | + if (isset($_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_URI'] === \OC::$WEBROOT && |
|
187 | + substr($_SERVER['REQUEST_URI'], -1) !== '/') { |
|
188 | + header('Location: '.\OC::$WEBROOT.'/'); |
|
189 | + exit(); |
|
190 | + } |
|
191 | + } |
|
192 | + |
|
193 | + // search the apps folder |
|
194 | + $config_paths = self::$config->getValue('apps_paths', array()); |
|
195 | + if (!empty($config_paths)) { |
|
196 | + foreach ($config_paths as $paths) { |
|
197 | + if (isset($paths['url']) && isset($paths['path'])) { |
|
198 | + $paths['url'] = rtrim($paths['url'], '/'); |
|
199 | + $paths['path'] = rtrim($paths['path'], '/'); |
|
200 | + OC::$APPSROOTS[] = $paths; |
|
201 | + } |
|
202 | + } |
|
203 | + } elseif (file_exists(OC::$SERVERROOT . '/apps')) { |
|
204 | + OC::$APPSROOTS[] = array('path' => OC::$SERVERROOT . '/apps', 'url' => '/apps', 'writable' => true); |
|
205 | + } elseif (file_exists(OC::$SERVERROOT . '/../apps')) { |
|
206 | + OC::$APPSROOTS[] = array( |
|
207 | + 'path' => rtrim(dirname(OC::$SERVERROOT), '/') . '/apps', |
|
208 | + 'url' => '/apps', |
|
209 | + 'writable' => true |
|
210 | + ); |
|
211 | + } |
|
212 | + |
|
213 | + if (empty(OC::$APPSROOTS)) { |
|
214 | + throw new \RuntimeException('apps directory not found! Please put the Nextcloud apps folder in the Nextcloud folder' |
|
215 | + . ' or the folder above. You can also configure the location in the config.php file.'); |
|
216 | + } |
|
217 | + $paths = array(); |
|
218 | + foreach (OC::$APPSROOTS as $path) { |
|
219 | + $paths[] = $path['path']; |
|
220 | + if (!is_dir($path['path'])) { |
|
221 | + throw new \RuntimeException(sprintf('App directory "%s" not found! Please put the Nextcloud apps folder in the' |
|
222 | + . ' Nextcloud folder or the folder above. You can also configure the location in the' |
|
223 | + . ' config.php file.', $path['path'])); |
|
224 | + } |
|
225 | + } |
|
226 | + |
|
227 | + // set the right include path |
|
228 | + set_include_path( |
|
229 | + implode(PATH_SEPARATOR, $paths) |
|
230 | + ); |
|
231 | + } |
|
232 | + |
|
233 | + public static function checkConfig() { |
|
234 | + $l = \OC::$server->getL10N('lib'); |
|
235 | + |
|
236 | + // Create config if it does not already exist |
|
237 | + $configFilePath = self::$configDir .'/config.php'; |
|
238 | + if(!file_exists($configFilePath)) { |
|
239 | + @touch($configFilePath); |
|
240 | + } |
|
241 | + |
|
242 | + // Check if config is writable |
|
243 | + $configFileWritable = is_writable($configFilePath); |
|
244 | + if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled() |
|
245 | + || !$configFileWritable && \OCP\Util::needUpgrade()) { |
|
246 | + |
|
247 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
248 | + |
|
249 | + if (self::$CLI) { |
|
250 | + echo $l->t('Cannot write into "config" directory!')."\n"; |
|
251 | + echo $l->t('This can usually be fixed by giving the webserver write access to the config directory')."\n"; |
|
252 | + echo "\n"; |
|
253 | + echo $l->t('See %s', [ $urlGenerator->linkToDocs('admin-dir_permissions') ])."\n"; |
|
254 | + exit; |
|
255 | + } else { |
|
256 | + OC_Template::printErrorPage( |
|
257 | + $l->t('Cannot write into "config" directory!'), |
|
258 | + $l->t('This can usually be fixed by giving the webserver write access to the config directory. See %s', |
|
259 | + [ $urlGenerator->linkToDocs('admin-dir_permissions') ]) |
|
260 | + ); |
|
261 | + } |
|
262 | + } |
|
263 | + } |
|
264 | + |
|
265 | + public static function checkInstalled() { |
|
266 | + if (defined('OC_CONSOLE')) { |
|
267 | + return; |
|
268 | + } |
|
269 | + // Redirect to installer if not installed |
|
270 | + if (!\OC::$server->getSystemConfig()->getValue('installed', false) && OC::$SUBURI !== '/index.php' && OC::$SUBURI !== '/status.php') { |
|
271 | + if (OC::$CLI) { |
|
272 | + throw new Exception('Not installed'); |
|
273 | + } else { |
|
274 | + $url = OC::$WEBROOT . '/index.php'; |
|
275 | + header('Location: ' . $url); |
|
276 | + } |
|
277 | + exit(); |
|
278 | + } |
|
279 | + } |
|
280 | + |
|
281 | + public static function checkMaintenanceMode() { |
|
282 | + // Allow ajax update script to execute without being stopped |
|
283 | + if (\OC::$server->getSystemConfig()->getValue('maintenance', false) && OC::$SUBURI != '/core/ajax/update.php') { |
|
284 | + // send http status 503 |
|
285 | + header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
286 | + header('Status: 503 Service Temporarily Unavailable'); |
|
287 | + header('Retry-After: 120'); |
|
288 | + |
|
289 | + // render error page |
|
290 | + $template = new OC_Template('', 'update.user', 'guest'); |
|
291 | + OC_Util::addScript('maintenance-check'); |
|
292 | + OC_Util::addStyle('core', 'guest'); |
|
293 | + $template->printPage(); |
|
294 | + die(); |
|
295 | + } |
|
296 | + } |
|
297 | + |
|
298 | + /** |
|
299 | + * Prints the upgrade page |
|
300 | + * |
|
301 | + * @param \OC\SystemConfig $systemConfig |
|
302 | + */ |
|
303 | + private static function printUpgradePage(\OC\SystemConfig $systemConfig) { |
|
304 | + $disableWebUpdater = $systemConfig->getValue('upgrade.disable-web', false); |
|
305 | + $tooBig = false; |
|
306 | + if (!$disableWebUpdater) { |
|
307 | + $apps = \OC::$server->getAppManager(); |
|
308 | + if ($apps->isInstalled('user_ldap')) { |
|
309 | + $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
|
310 | + |
|
311 | + $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') |
|
312 | + ->from('ldap_user_mapping') |
|
313 | + ->execute(); |
|
314 | + $row = $result->fetch(); |
|
315 | + $result->closeCursor(); |
|
316 | + |
|
317 | + $tooBig = ($row['user_count'] > 50); |
|
318 | + } |
|
319 | + if (!$tooBig && $apps->isInstalled('user_saml')) { |
|
320 | + $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); |
|
321 | + |
|
322 | + $result = $qb->selectAlias($qb->createFunction('COUNT(*)'), 'user_count') |
|
323 | + ->from('user_saml_users') |
|
324 | + ->execute(); |
|
325 | + $row = $result->fetch(); |
|
326 | + $result->closeCursor(); |
|
327 | + |
|
328 | + $tooBig = ($row['user_count'] > 50); |
|
329 | + } |
|
330 | + if (!$tooBig) { |
|
331 | + // count users |
|
332 | + $stats = \OC::$server->getUserManager()->countUsers(); |
|
333 | + $totalUsers = array_sum($stats); |
|
334 | + $tooBig = ($totalUsers > 50); |
|
335 | + } |
|
336 | + } |
|
337 | + $ignoreTooBigWarning = isset($_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup']) && |
|
338 | + $_GET['IKnowThatThisIsABigInstanceAndTheUpdateRequestCouldRunIntoATimeoutAndHowToRestoreABackup'] === 'IAmSuperSureToDoThis'; |
|
339 | + |
|
340 | + if ($disableWebUpdater || ($tooBig && !$ignoreTooBigWarning)) { |
|
341 | + // send http status 503 |
|
342 | + header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
343 | + header('Status: 503 Service Temporarily Unavailable'); |
|
344 | + header('Retry-After: 120'); |
|
345 | + |
|
346 | + // render error page |
|
347 | + $template = new OC_Template('', 'update.use-cli', 'guest'); |
|
348 | + $template->assign('productName', 'nextcloud'); // for now |
|
349 | + $template->assign('version', OC_Util::getVersionString()); |
|
350 | + $template->assign('tooBig', $tooBig); |
|
351 | + |
|
352 | + $template->printPage(); |
|
353 | + die(); |
|
354 | + } |
|
355 | + |
|
356 | + // check whether this is a core update or apps update |
|
357 | + $installedVersion = $systemConfig->getValue('version', '0.0.0'); |
|
358 | + $currentVersion = implode('.', \OCP\Util::getVersion()); |
|
359 | + |
|
360 | + // if not a core upgrade, then it's apps upgrade |
|
361 | + $isAppsOnlyUpgrade = version_compare($currentVersion, $installedVersion, '='); |
|
362 | + |
|
363 | + $oldTheme = $systemConfig->getValue('theme'); |
|
364 | + $systemConfig->setValue('theme', ''); |
|
365 | + OC_Util::addScript('config'); // needed for web root |
|
366 | + OC_Util::addScript('update'); |
|
367 | + |
|
368 | + /** @var \OC\App\AppManager $appManager */ |
|
369 | + $appManager = \OC::$server->getAppManager(); |
|
370 | + |
|
371 | + $tmpl = new OC_Template('', 'update.admin', 'guest'); |
|
372 | + $tmpl->assign('version', OC_Util::getVersionString()); |
|
373 | + $tmpl->assign('isAppsOnlyUpgrade', $isAppsOnlyUpgrade); |
|
374 | + |
|
375 | + // get third party apps |
|
376 | + $ocVersion = \OCP\Util::getVersion(); |
|
377 | + $ocVersion = implode('.', $ocVersion); |
|
378 | + $incompatibleApps = $appManager->getIncompatibleApps($ocVersion); |
|
379 | + $incompatibleShippedApps = []; |
|
380 | + foreach ($incompatibleApps as $appInfo) { |
|
381 | + if ($appManager->isShipped($appInfo['id'])) { |
|
382 | + $incompatibleShippedApps[] = $appInfo['name'] . ' (' . $appInfo['id'] . ')'; |
|
383 | + } |
|
384 | + } |
|
385 | + |
|
386 | + if (!empty($incompatibleShippedApps)) { |
|
387 | + $l = \OC::$server->getL10N('core'); |
|
388 | + $hint = $l->t('The files of the app %$1s were not replaced correctly. Make sure it is a version compatible with the server.', [implode(', ', $incompatibleShippedApps)]); |
|
389 | + throw new \OC\HintException('The files of the app ' . implode(', ', $incompatibleShippedApps) . ' were not replaced correctly. Make sure it is a version compatible with the server.', $hint); |
|
390 | + } |
|
391 | + |
|
392 | + $tmpl->assign('appsToUpgrade', $appManager->getAppsNeedingUpgrade($ocVersion)); |
|
393 | + $tmpl->assign('incompatibleAppsList', $incompatibleApps); |
|
394 | + $tmpl->assign('productName', 'Nextcloud'); // for now |
|
395 | + $tmpl->assign('oldTheme', $oldTheme); |
|
396 | + $tmpl->printPage(); |
|
397 | + } |
|
398 | + |
|
399 | + public static function initSession() { |
|
400 | + if(self::$server->getRequest()->getServerProtocol() === 'https') { |
|
401 | + ini_set('session.cookie_secure', true); |
|
402 | + } |
|
403 | + |
|
404 | + // prevents javascript from accessing php session cookies |
|
405 | + ini_set('session.cookie_httponly', 'true'); |
|
406 | + |
|
407 | + // set the cookie path to the Nextcloud directory |
|
408 | + $cookie_path = OC::$WEBROOT ? : '/'; |
|
409 | + ini_set('session.cookie_path', $cookie_path); |
|
410 | + |
|
411 | + // Let the session name be changed in the initSession Hook |
|
412 | + $sessionName = OC_Util::getInstanceId(); |
|
413 | + |
|
414 | + try { |
|
415 | + // Allow session apps to create a custom session object |
|
416 | + $useCustomSession = false; |
|
417 | + $session = self::$server->getSession(); |
|
418 | + OC_Hook::emit('OC', 'initSession', array('session' => &$session, 'sessionName' => &$sessionName, 'useCustomSession' => &$useCustomSession)); |
|
419 | + if (!$useCustomSession) { |
|
420 | + // set the session name to the instance id - which is unique |
|
421 | + $session = new \OC\Session\Internal($sessionName); |
|
422 | + } |
|
423 | + |
|
424 | + $cryptoWrapper = \OC::$server->getSessionCryptoWrapper(); |
|
425 | + $session = $cryptoWrapper->wrapSession($session); |
|
426 | + self::$server->setSession($session); |
|
427 | + |
|
428 | + // if session can't be started break with http 500 error |
|
429 | + } catch (Exception $e) { |
|
430 | + \OC::$server->getLogger()->logException($e, ['app' => 'base']); |
|
431 | + //show the user a detailed error page |
|
432 | + OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR); |
|
433 | + OC_Template::printExceptionErrorPage($e); |
|
434 | + die(); |
|
435 | + } |
|
436 | + |
|
437 | + $sessionLifeTime = self::getSessionLifeTime(); |
|
438 | + |
|
439 | + // session timeout |
|
440 | + if ($session->exists('LAST_ACTIVITY') && (time() - $session->get('LAST_ACTIVITY') > $sessionLifeTime)) { |
|
441 | + if (isset($_COOKIE[session_name()])) { |
|
442 | + setcookie(session_name(), null, -1, self::$WEBROOT ? : '/'); |
|
443 | + } |
|
444 | + \OC::$server->getUserSession()->logout(); |
|
445 | + } |
|
446 | + |
|
447 | + $session->set('LAST_ACTIVITY', time()); |
|
448 | + } |
|
449 | + |
|
450 | + /** |
|
451 | + * @return string |
|
452 | + */ |
|
453 | + private static function getSessionLifeTime() { |
|
454 | + return \OC::$server->getConfig()->getSystemValue('session_lifetime', 60 * 60 * 24); |
|
455 | + } |
|
456 | + |
|
457 | + public static function loadAppClassPaths() { |
|
458 | + foreach (OC_App::getEnabledApps() as $app) { |
|
459 | + $appPath = OC_App::getAppPath($app); |
|
460 | + if ($appPath === false) { |
|
461 | + continue; |
|
462 | + } |
|
463 | + |
|
464 | + $file = $appPath . '/appinfo/classpath.php'; |
|
465 | + if (file_exists($file)) { |
|
466 | + require_once $file; |
|
467 | + } |
|
468 | + } |
|
469 | + } |
|
470 | + |
|
471 | + /** |
|
472 | + * Try to set some values to the required Nextcloud default |
|
473 | + */ |
|
474 | + public static function setRequiredIniValues() { |
|
475 | + @ini_set('default_charset', 'UTF-8'); |
|
476 | + @ini_set('gd.jpeg_ignore_warning', '1'); |
|
477 | + } |
|
478 | + |
|
479 | + /** |
|
480 | + * Send the same site cookies |
|
481 | + */ |
|
482 | + private static function sendSameSiteCookies() { |
|
483 | + $cookieParams = session_get_cookie_params(); |
|
484 | + $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : ''; |
|
485 | + $policies = [ |
|
486 | + 'lax', |
|
487 | + 'strict', |
|
488 | + ]; |
|
489 | + |
|
490 | + // Append __Host to the cookie if it meets the requirements |
|
491 | + $cookiePrefix = ''; |
|
492 | + if($cookieParams['secure'] === true && $cookieParams['path'] === '/') { |
|
493 | + $cookiePrefix = '__Host-'; |
|
494 | + } |
|
495 | + |
|
496 | + foreach($policies as $policy) { |
|
497 | + header( |
|
498 | + sprintf( |
|
499 | + 'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s', |
|
500 | + $cookiePrefix, |
|
501 | + $policy, |
|
502 | + $cookieParams['path'], |
|
503 | + $policy |
|
504 | + ), |
|
505 | + false |
|
506 | + ); |
|
507 | + } |
|
508 | + } |
|
509 | + |
|
510 | + /** |
|
511 | + * Same Site cookie to further mitigate CSRF attacks. This cookie has to |
|
512 | + * be set in every request if cookies are sent to add a second level of |
|
513 | + * defense against CSRF. |
|
514 | + * |
|
515 | + * If the cookie is not sent this will set the cookie and reload the page. |
|
516 | + * We use an additional cookie since we want to protect logout CSRF and |
|
517 | + * also we can't directly interfere with PHP's session mechanism. |
|
518 | + */ |
|
519 | + private static function performSameSiteCookieProtection() { |
|
520 | + $request = \OC::$server->getRequest(); |
|
521 | + |
|
522 | + // Some user agents are notorious and don't really properly follow HTTP |
|
523 | + // specifications. For those, have an automated opt-out. Since the protection |
|
524 | + // for remote.php is applied in base.php as starting point we need to opt out |
|
525 | + // here. |
|
526 | + $incompatibleUserAgents = [ |
|
527 | + // OS X Finder |
|
528 | + '/^WebDAVFS/', |
|
529 | + '/^Microsoft-WebDAV-MiniRedir/', |
|
530 | + ]; |
|
531 | + if($request->isUserAgent($incompatibleUserAgents)) { |
|
532 | + return; |
|
533 | + } |
|
534 | + |
|
535 | + if(count($_COOKIE) > 0) { |
|
536 | + $requestUri = $request->getScriptName(); |
|
537 | + $processingScript = explode('/', $requestUri); |
|
538 | + $processingScript = $processingScript[count($processingScript)-1]; |
|
539 | + |
|
540 | + // index.php routes are handled in the middleware |
|
541 | + if($processingScript === 'index.php') { |
|
542 | + return; |
|
543 | + } |
|
544 | + |
|
545 | + // All other endpoints require the lax and the strict cookie |
|
546 | + if(!$request->passesStrictCookieCheck()) { |
|
547 | + self::sendSameSiteCookies(); |
|
548 | + // Debug mode gets access to the resources without strict cookie |
|
549 | + // due to the fact that the SabreDAV browser also lives there. |
|
550 | + if(!\OC::$server->getConfig()->getSystemValue('debug', false)) { |
|
551 | + http_response_code(\OCP\AppFramework\Http::STATUS_SERVICE_UNAVAILABLE); |
|
552 | + exit(); |
|
553 | + } |
|
554 | + } |
|
555 | + } elseif(!isset($_COOKIE['nc_sameSiteCookielax']) || !isset($_COOKIE['nc_sameSiteCookiestrict'])) { |
|
556 | + self::sendSameSiteCookies(); |
|
557 | + } |
|
558 | + } |
|
559 | + |
|
560 | + public static function init() { |
|
561 | + // calculate the root directories |
|
562 | + OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4)); |
|
563 | + |
|
564 | + // register autoloader |
|
565 | + $loaderStart = microtime(true); |
|
566 | + require_once __DIR__ . '/autoloader.php'; |
|
567 | + self::$loader = new \OC\Autoloader([ |
|
568 | + OC::$SERVERROOT . '/lib/private/legacy', |
|
569 | + ]); |
|
570 | + if (defined('PHPUNIT_RUN')) { |
|
571 | + self::$loader->addValidRoot(OC::$SERVERROOT . '/tests'); |
|
572 | + } |
|
573 | + spl_autoload_register(array(self::$loader, 'load')); |
|
574 | + $loaderEnd = microtime(true); |
|
575 | + |
|
576 | + self::$CLI = (php_sapi_name() == 'cli'); |
|
577 | + |
|
578 | + // Add default composer PSR-4 autoloader |
|
579 | + self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php'; |
|
580 | + |
|
581 | + try { |
|
582 | + self::initPaths(); |
|
583 | + // setup 3rdparty autoloader |
|
584 | + $vendorAutoLoad = OC::$SERVERROOT. '/3rdparty/autoload.php'; |
|
585 | + if (!file_exists($vendorAutoLoad)) { |
|
586 | + throw new \RuntimeException('Composer autoloader not found, unable to continue. Check the folder "3rdparty". Running "git submodule update --init" will initialize the git submodule that handles the subfolder "3rdparty".'); |
|
587 | + } |
|
588 | + require_once $vendorAutoLoad; |
|
589 | + |
|
590 | + } catch (\RuntimeException $e) { |
|
591 | + if (!self::$CLI) { |
|
592 | + $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']); |
|
593 | + $protocol = in_array($claimedProtocol, ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2']) ? $claimedProtocol : 'HTTP/1.1'; |
|
594 | + header($protocol . ' ' . OC_Response::STATUS_SERVICE_UNAVAILABLE); |
|
595 | + } |
|
596 | + // we can't use the template error page here, because this needs the |
|
597 | + // DI container which isn't available yet |
|
598 | + print($e->getMessage()); |
|
599 | + exit(); |
|
600 | + } |
|
601 | + |
|
602 | + // setup the basic server |
|
603 | + self::$server = new \OC\Server(\OC::$WEBROOT, self::$config); |
|
604 | + \OC::$server->getEventLogger()->log('autoloader', 'Autoloader', $loaderStart, $loaderEnd); |
|
605 | + \OC::$server->getEventLogger()->start('boot', 'Initialize'); |
|
606 | + |
|
607 | + // Don't display errors and log them |
|
608 | + error_reporting(E_ALL | E_STRICT); |
|
609 | + @ini_set('display_errors', '0'); |
|
610 | + @ini_set('log_errors', '1'); |
|
611 | + |
|
612 | + if(!date_default_timezone_set('UTC')) { |
|
613 | + throw new \RuntimeException('Could not set timezone to UTC'); |
|
614 | + } |
|
615 | + |
|
616 | + //try to configure php to enable big file uploads. |
|
617 | + //this doesn´t work always depending on the webserver and php configuration. |
|
618 | + //Let´s try to overwrite some defaults anyway |
|
619 | + |
|
620 | + //try to set the maximum execution time to 60min |
|
621 | + if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { |
|
622 | + @set_time_limit(3600); |
|
623 | + } |
|
624 | + @ini_set('max_execution_time', '3600'); |
|
625 | + @ini_set('max_input_time', '3600'); |
|
626 | + |
|
627 | + //try to set the maximum filesize to 10G |
|
628 | + @ini_set('upload_max_filesize', '10G'); |
|
629 | + @ini_set('post_max_size', '10G'); |
|
630 | + @ini_set('file_uploads', '50'); |
|
631 | + |
|
632 | + self::setRequiredIniValues(); |
|
633 | + self::handleAuthHeaders(); |
|
634 | + self::registerAutoloaderCache(); |
|
635 | + |
|
636 | + // initialize intl fallback is necessary |
|
637 | + \Patchwork\Utf8\Bootup::initIntl(); |
|
638 | + OC_Util::isSetLocaleWorking(); |
|
639 | + |
|
640 | + if (!defined('PHPUNIT_RUN')) { |
|
641 | + OC\Log\ErrorHandler::setLogger(\OC::$server->getLogger()); |
|
642 | + $debug = \OC::$server->getConfig()->getSystemValue('debug', false); |
|
643 | + OC\Log\ErrorHandler::register($debug); |
|
644 | + } |
|
645 | + |
|
646 | + \OC::$server->getEventLogger()->start('init_session', 'Initialize session'); |
|
647 | + OC_App::loadApps(array('session')); |
|
648 | + if (!self::$CLI) { |
|
649 | + self::initSession(); |
|
650 | + } |
|
651 | + \OC::$server->getEventLogger()->end('init_session'); |
|
652 | + self::checkConfig(); |
|
653 | + self::checkInstalled(); |
|
654 | + |
|
655 | + OC_Response::addSecurityHeaders(); |
|
656 | + |
|
657 | + self::performSameSiteCookieProtection(); |
|
658 | + |
|
659 | + if (!defined('OC_CONSOLE')) { |
|
660 | + $errors = OC_Util::checkServer(\OC::$server->getSystemConfig()); |
|
661 | + if (count($errors) > 0) { |
|
662 | + if (self::$CLI) { |
|
663 | + // Convert l10n string into regular string for usage in database |
|
664 | + $staticErrors = []; |
|
665 | + foreach ($errors as $error) { |
|
666 | + echo $error['error'] . "\n"; |
|
667 | + echo $error['hint'] . "\n\n"; |
|
668 | + $staticErrors[] = [ |
|
669 | + 'error' => (string)$error['error'], |
|
670 | + 'hint' => (string)$error['hint'], |
|
671 | + ]; |
|
672 | + } |
|
673 | + |
|
674 | + try { |
|
675 | + \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors)); |
|
676 | + } catch (\Exception $e) { |
|
677 | + echo('Writing to database failed'); |
|
678 | + } |
|
679 | + exit(1); |
|
680 | + } else { |
|
681 | + OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE); |
|
682 | + OC_Util::addStyle('guest'); |
|
683 | + OC_Template::printGuestPage('', 'error', array('errors' => $errors)); |
|
684 | + exit; |
|
685 | + } |
|
686 | + } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) { |
|
687 | + \OC::$server->getConfig()->deleteAppValue('core', 'cronErrors'); |
|
688 | + } |
|
689 | + } |
|
690 | + //try to set the session lifetime |
|
691 | + $sessionLifeTime = self::getSessionLifeTime(); |
|
692 | + @ini_set('gc_maxlifetime', (string)$sessionLifeTime); |
|
693 | + |
|
694 | + $systemConfig = \OC::$server->getSystemConfig(); |
|
695 | + |
|
696 | + // User and Groups |
|
697 | + if (!$systemConfig->getValue("installed", false)) { |
|
698 | + self::$server->getSession()->set('user_id', ''); |
|
699 | + } |
|
700 | + |
|
701 | + OC_User::useBackend(new \OC\User\Database()); |
|
702 | + \OC::$server->getGroupManager()->addBackend(new \OC\Group\Database()); |
|
703 | + |
|
704 | + // Subscribe to the hook |
|
705 | + \OCP\Util::connectHook( |
|
706 | + '\OCA\Files_Sharing\API\Server2Server', |
|
707 | + 'preLoginNameUsedAsUserName', |
|
708 | + '\OC\User\Database', |
|
709 | + 'preLoginNameUsedAsUserName' |
|
710 | + ); |
|
711 | + |
|
712 | + //setup extra user backends |
|
713 | + if (!\OCP\Util::needUpgrade()) { |
|
714 | + OC_User::setupBackends(); |
|
715 | + } else { |
|
716 | + // Run upgrades in incognito mode |
|
717 | + OC_User::setIncognitoMode(true); |
|
718 | + } |
|
719 | + |
|
720 | + self::registerCleanupHooks(); |
|
721 | + self::registerFilesystemHooks(); |
|
722 | + self::registerShareHooks(); |
|
723 | + self::registerEncryptionWrapper(); |
|
724 | + self::registerEncryptionHooks(); |
|
725 | + self::registerAccountHooks(); |
|
726 | + |
|
727 | + // Make sure that the application class is not loaded before the database is setup |
|
728 | + if ($systemConfig->getValue("installed", false)) { |
|
729 | + $settings = new \OC\Settings\Application(); |
|
730 | + $settings->register(); |
|
731 | + } |
|
732 | + |
|
733 | + //make sure temporary files are cleaned up |
|
734 | + $tmpManager = \OC::$server->getTempManager(); |
|
735 | + register_shutdown_function(array($tmpManager, 'clean')); |
|
736 | + $lockProvider = \OC::$server->getLockingProvider(); |
|
737 | + register_shutdown_function(array($lockProvider, 'releaseAll')); |
|
738 | + |
|
739 | + // Check whether the sample configuration has been copied |
|
740 | + if($systemConfig->getValue('copied_sample_config', false)) { |
|
741 | + $l = \OC::$server->getL10N('lib'); |
|
742 | + header('HTTP/1.1 503 Service Temporarily Unavailable'); |
|
743 | + header('Status: 503 Service Temporarily Unavailable'); |
|
744 | + OC_Template::printErrorPage( |
|
745 | + $l->t('Sample configuration detected'), |
|
746 | + $l->t('It has been detected that the sample configuration has been copied. This can break your installation and is unsupported. Please read the documentation before performing changes on config.php') |
|
747 | + ); |
|
748 | + return; |
|
749 | + } |
|
750 | + |
|
751 | + $request = \OC::$server->getRequest(); |
|
752 | + $host = $request->getInsecureServerHost(); |
|
753 | + /** |
|
754 | + * if the host passed in headers isn't trusted |
|
755 | + * FIXME: Should not be in here at all :see_no_evil: |
|
756 | + */ |
|
757 | + if (!OC::$CLI |
|
758 | + // overwritehost is always trusted, workaround to not have to make |
|
759 | + // \OC\AppFramework\Http\Request::getOverwriteHost public |
|
760 | + && self::$server->getConfig()->getSystemValue('overwritehost') === '' |
|
761 | + && !\OC::$server->getTrustedDomainHelper()->isTrustedDomain($host) |
|
762 | + && self::$server->getConfig()->getSystemValue('installed', false) |
|
763 | + ) { |
|
764 | + // Allow access to CSS resources |
|
765 | + $isScssRequest = false; |
|
766 | + if(strpos($request->getPathInfo(), '/css/') === 0) { |
|
767 | + $isScssRequest = true; |
|
768 | + } |
|
769 | + |
|
770 | + if(substr($request->getRequestUri(), -11) === '/status.php') { |
|
771 | + OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST); |
|
772 | + header('Status: 400 Bad Request'); |
|
773 | + header('Content-Type: application/json'); |
|
774 | + echo '{"error": "Trusted domain error.", "code": 15}'; |
|
775 | + exit(); |
|
776 | + } |
|
777 | + |
|
778 | + if (!$isScssRequest) { |
|
779 | + OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST); |
|
780 | + header('Status: 400 Bad Request'); |
|
781 | + |
|
782 | + \OC::$server->getLogger()->info( |
|
783 | + 'Trusted domain error. "{remoteAddress}" tried to access using "{host}" as host.', |
|
784 | + [ |
|
785 | + 'app' => 'core', |
|
786 | + 'remoteAddress' => $request->getRemoteAddress(), |
|
787 | + 'host' => $host, |
|
788 | + ] |
|
789 | + ); |
|
790 | + |
|
791 | + $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); |
|
792 | + $tmpl->assign('docUrl', \OC::$server->getURLGenerator()->linkToDocs('admin-trusted-domains')); |
|
793 | + $tmpl->printPage(); |
|
794 | + |
|
795 | + exit(); |
|
796 | + } |
|
797 | + } |
|
798 | + \OC::$server->getEventLogger()->end('boot'); |
|
799 | + } |
|
800 | + |
|
801 | + /** |
|
802 | + * register hooks for the cleanup of cache and bruteforce protection |
|
803 | + */ |
|
804 | + public static function registerCleanupHooks() { |
|
805 | + //don't try to do this before we are properly setup |
|
806 | + if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) { |
|
807 | + |
|
808 | + // NOTE: This will be replaced to use OCP |
|
809 | + $userSession = self::$server->getUserSession(); |
|
810 | + $userSession->listen('\OC\User', 'postLogin', function () use ($userSession) { |
|
811 | + if (!defined('PHPUNIT_RUN')) { |
|
812 | + // reset brute force delay for this IP address and username |
|
813 | + $uid = \OC::$server->getUserSession()->getUser()->getUID(); |
|
814 | + $request = \OC::$server->getRequest(); |
|
815 | + $throttler = \OC::$server->getBruteForceThrottler(); |
|
816 | + $throttler->resetDelay($request->getRemoteAddress(), 'login', ['user' => $uid]); |
|
817 | + } |
|
818 | + |
|
819 | + try { |
|
820 | + $cache = new \OC\Cache\File(); |
|
821 | + $cache->gc(); |
|
822 | + } catch (\OC\ServerNotAvailableException $e) { |
|
823 | + // not a GC exception, pass it on |
|
824 | + throw $e; |
|
825 | + } catch (\OC\ForbiddenException $e) { |
|
826 | + // filesystem blocked for this request, ignore |
|
827 | + } catch (\Exception $e) { |
|
828 | + // a GC exception should not prevent users from using OC, |
|
829 | + // so log the exception |
|
830 | + \OC::$server->getLogger()->logException($e, [ |
|
831 | + 'message' => 'Exception when running cache gc.', |
|
832 | + 'level' => ILogger::WARN, |
|
833 | + 'app' => 'core', |
|
834 | + ]); |
|
835 | + } |
|
836 | + }); |
|
837 | + } |
|
838 | + } |
|
839 | + |
|
840 | + private static function registerEncryptionWrapper() { |
|
841 | + $manager = self::$server->getEncryptionManager(); |
|
842 | + \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage'); |
|
843 | + } |
|
844 | + |
|
845 | + private static function registerEncryptionHooks() { |
|
846 | + $enabled = self::$server->getEncryptionManager()->isEnabled(); |
|
847 | + if ($enabled) { |
|
848 | + \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared'); |
|
849 | + \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared'); |
|
850 | + \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename'); |
|
851 | + \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore'); |
|
852 | + } |
|
853 | + } |
|
854 | + |
|
855 | + private static function registerAccountHooks() { |
|
856 | + $hookHandler = new \OC\Accounts\Hooks(\OC::$server->getLogger()); |
|
857 | + \OCP\Util::connectHook('OC_User', 'changeUser', $hookHandler, 'changeUserHook'); |
|
858 | + } |
|
859 | + |
|
860 | + /** |
|
861 | + * register hooks for the filesystem |
|
862 | + */ |
|
863 | + public static function registerFilesystemHooks() { |
|
864 | + // Check for blacklisted files |
|
865 | + OC_Hook::connect('OC_Filesystem', 'write', Filesystem::class, 'isBlacklisted'); |
|
866 | + OC_Hook::connect('OC_Filesystem', 'rename', Filesystem::class, 'isBlacklisted'); |
|
867 | + } |
|
868 | + |
|
869 | + /** |
|
870 | + * register hooks for sharing |
|
871 | + */ |
|
872 | + public static function registerShareHooks() { |
|
873 | + if (\OC::$server->getSystemConfig()->getValue('installed')) { |
|
874 | + OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser'); |
|
875 | + OC_Hook::connect('OC_User', 'post_removeFromGroup', Hooks::class, 'post_removeFromGroup'); |
|
876 | + OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup'); |
|
877 | + } |
|
878 | + } |
|
879 | + |
|
880 | + protected static function registerAutoloaderCache() { |
|
881 | + // The class loader takes an optional low-latency cache, which MUST be |
|
882 | + // namespaced. The instanceid is used for namespacing, but might be |
|
883 | + // unavailable at this point. Furthermore, it might not be possible to |
|
884 | + // generate an instanceid via \OC_Util::getInstanceId() because the |
|
885 | + // config file may not be writable. As such, we only register a class |
|
886 | + // loader cache if instanceid is available without trying to create one. |
|
887 | + $instanceId = \OC::$server->getSystemConfig()->getValue('instanceid', null); |
|
888 | + if ($instanceId) { |
|
889 | + try { |
|
890 | + $memcacheFactory = \OC::$server->getMemCacheFactory(); |
|
891 | + self::$loader->setMemoryCache($memcacheFactory->createLocal('Autoloader')); |
|
892 | + } catch (\Exception $ex) { |
|
893 | + } |
|
894 | + } |
|
895 | + } |
|
896 | + |
|
897 | + /** |
|
898 | + * Handle the request |
|
899 | + */ |
|
900 | + public static function handleRequest() { |
|
901 | + |
|
902 | + \OC::$server->getEventLogger()->start('handle_request', 'Handle request'); |
|
903 | + $systemConfig = \OC::$server->getSystemConfig(); |
|
904 | + // load all the classpaths from the enabled apps so they are available |
|
905 | + // in the routing files of each app |
|
906 | + OC::loadAppClassPaths(); |
|
907 | + |
|
908 | + // Check if Nextcloud is installed or in maintenance (update) mode |
|
909 | + if (!$systemConfig->getValue('installed', false)) { |
|
910 | + \OC::$server->getSession()->clear(); |
|
911 | + $setupHelper = new OC\Setup( |
|
912 | + $systemConfig, |
|
913 | + \OC::$server->getIniWrapper(), |
|
914 | + \OC::$server->getL10N('lib'), |
|
915 | + \OC::$server->query(\OCP\Defaults::class), |
|
916 | + \OC::$server->getLogger(), |
|
917 | + \OC::$server->getSecureRandom(), |
|
918 | + \OC::$server->query(\OC\Installer::class) |
|
919 | + ); |
|
920 | + $controller = new OC\Core\Controller\SetupController($setupHelper); |
|
921 | + $controller->run($_POST); |
|
922 | + exit(); |
|
923 | + } |
|
924 | + |
|
925 | + $request = \OC::$server->getRequest(); |
|
926 | + $requestPath = $request->getRawPathInfo(); |
|
927 | + if ($requestPath === '/heartbeat') { |
|
928 | + return; |
|
929 | + } |
|
930 | + if (substr($requestPath, -3) !== '.js') { // we need these files during the upgrade |
|
931 | + self::checkMaintenanceMode(); |
|
932 | + |
|
933 | + if (\OCP\Util::needUpgrade()) { |
|
934 | + if (function_exists('opcache_reset')) { |
|
935 | + opcache_reset(); |
|
936 | + } |
|
937 | + if (!$systemConfig->getValue('maintenance', false)) { |
|
938 | + self::printUpgradePage($systemConfig); |
|
939 | + exit(); |
|
940 | + } |
|
941 | + } |
|
942 | + } |
|
943 | + |
|
944 | + // emergency app disabling |
|
945 | + if ($requestPath === '/disableapp' |
|
946 | + && $request->getMethod() === 'POST' |
|
947 | + && ((array)$request->getParam('appid')) !== '' |
|
948 | + ) { |
|
949 | + \OC_JSON::callCheck(); |
|
950 | + \OC_JSON::checkAdminUser(); |
|
951 | + $appIds = (array)$request->getParam('appid'); |
|
952 | + foreach($appIds as $appId) { |
|
953 | + $appId = \OC_App::cleanAppId($appId); |
|
954 | + \OC::$server->getAppManager()->disableApp($appId); |
|
955 | + } |
|
956 | + \OC_JSON::success(); |
|
957 | + exit(); |
|
958 | + } |
|
959 | + |
|
960 | + // Always load authentication apps |
|
961 | + OC_App::loadApps(['authentication']); |
|
962 | + |
|
963 | + // Load minimum set of apps |
|
964 | + if (!\OCP\Util::needUpgrade() |
|
965 | + && !$systemConfig->getValue('maintenance', false)) { |
|
966 | + // For logged-in users: Load everything |
|
967 | + if(\OC::$server->getUserSession()->isLoggedIn()) { |
|
968 | + OC_App::loadApps(); |
|
969 | + } else { |
|
970 | + // For guests: Load only filesystem and logging |
|
971 | + OC_App::loadApps(array('filesystem', 'logging')); |
|
972 | + self::handleLogin($request); |
|
973 | + } |
|
974 | + } |
|
975 | + |
|
976 | + if (!self::$CLI) { |
|
977 | + try { |
|
978 | + if (!$systemConfig->getValue('maintenance', false) && !\OCP\Util::needUpgrade()) { |
|
979 | + OC_App::loadApps(array('filesystem', 'logging')); |
|
980 | + OC_App::loadApps(); |
|
981 | + } |
|
982 | + OC_Util::setupFS(); |
|
983 | + OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo()); |
|
984 | + return; |
|
985 | + } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { |
|
986 | + //header('HTTP/1.0 404 Not Found'); |
|
987 | + } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { |
|
988 | + OC_Response::setStatus(405); |
|
989 | + return; |
|
990 | + } |
|
991 | + } |
|
992 | + |
|
993 | + // Handle WebDAV |
|
994 | + if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { |
|
995 | + // not allowed any more to prevent people |
|
996 | + // mounting this root directly. |
|
997 | + // Users need to mount remote.php/webdav instead. |
|
998 | + header('HTTP/1.1 405 Method Not Allowed'); |
|
999 | + header('Status: 405 Method Not Allowed'); |
|
1000 | + return; |
|
1001 | + } |
|
1002 | + |
|
1003 | + // Someone is logged in |
|
1004 | + if (\OC::$server->getUserSession()->isLoggedIn()) { |
|
1005 | + OC_App::loadApps(); |
|
1006 | + OC_User::setupBackends(); |
|
1007 | + OC_Util::setupFS(); |
|
1008 | + // FIXME |
|
1009 | + // Redirect to default application |
|
1010 | + OC_Util::redirectToDefaultPage(); |
|
1011 | + } else { |
|
1012 | + // Not handled and not logged in |
|
1013 | + header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm')); |
|
1014 | + } |
|
1015 | + } |
|
1016 | + |
|
1017 | + /** |
|
1018 | + * Check login: apache auth, auth token, basic auth |
|
1019 | + * |
|
1020 | + * @param OCP\IRequest $request |
|
1021 | + * @return boolean |
|
1022 | + */ |
|
1023 | + static function handleLogin(OCP\IRequest $request) { |
|
1024 | + $userSession = self::$server->getUserSession(); |
|
1025 | + if (OC_User::handleApacheAuth()) { |
|
1026 | + return true; |
|
1027 | + } |
|
1028 | + if ($userSession->tryTokenLogin($request)) { |
|
1029 | + return true; |
|
1030 | + } |
|
1031 | + if (isset($_COOKIE['nc_username']) |
|
1032 | + && isset($_COOKIE['nc_token']) |
|
1033 | + && isset($_COOKIE['nc_session_id']) |
|
1034 | + && $userSession->loginWithCookie($_COOKIE['nc_username'], $_COOKIE['nc_token'], $_COOKIE['nc_session_id'])) { |
|
1035 | + return true; |
|
1036 | + } |
|
1037 | + if ($userSession->tryBasicAuthLogin($request, \OC::$server->getBruteForceThrottler())) { |
|
1038 | + return true; |
|
1039 | + } |
|
1040 | + return false; |
|
1041 | + } |
|
1042 | + |
|
1043 | + protected static function handleAuthHeaders() { |
|
1044 | + //copy http auth headers for apache+php-fcgid work around |
|
1045 | + if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) { |
|
1046 | + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION']; |
|
1047 | + } |
|
1048 | + |
|
1049 | + // Extract PHP_AUTH_USER/PHP_AUTH_PW from other headers if necessary. |
|
1050 | + $vars = array( |
|
1051 | + 'HTTP_AUTHORIZATION', // apache+php-cgi work around |
|
1052 | + 'REDIRECT_HTTP_AUTHORIZATION', // apache+php-cgi alternative |
|
1053 | + ); |
|
1054 | + foreach ($vars as $var) { |
|
1055 | + if (isset($_SERVER[$var]) && preg_match('/Basic\s+(.*)$/i', $_SERVER[$var], $matches)) { |
|
1056 | + list($name, $password) = explode(':', base64_decode($matches[1]), 2); |
|
1057 | + $_SERVER['PHP_AUTH_USER'] = $name; |
|
1058 | + $_SERVER['PHP_AUTH_PW'] = $password; |
|
1059 | + break; |
|
1060 | + } |
|
1061 | + } |
|
1062 | + } |
|
1063 | 1063 | } |
1064 | 1064 | |
1065 | 1065 | OC::init(); |
@@ -37,151 +37,151 @@ |
||
37 | 37 | use OCP\ILogger; |
38 | 38 | |
39 | 39 | class Autoloader { |
40 | - /** @var bool */ |
|
41 | - private $useGlobalClassPath = true; |
|
42 | - /** @var array */ |
|
43 | - private $validRoots = []; |
|
44 | - |
|
45 | - /** |
|
46 | - * Optional low-latency memory cache for class to path mapping. |
|
47 | - * |
|
48 | - * @var \OC\Memcache\Cache |
|
49 | - */ |
|
50 | - protected $memoryCache; |
|
51 | - |
|
52 | - /** |
|
53 | - * Autoloader constructor. |
|
54 | - * |
|
55 | - * @param string[] $validRoots |
|
56 | - */ |
|
57 | - public function __construct(array $validRoots) { |
|
58 | - foreach ($validRoots as $root) { |
|
59 | - $this->validRoots[$root] = true; |
|
60 | - } |
|
61 | - } |
|
62 | - |
|
63 | - /** |
|
64 | - * Add a path to the list of valid php roots for auto loading |
|
65 | - * |
|
66 | - * @param string $root |
|
67 | - */ |
|
68 | - public function addValidRoot(string $root) { |
|
69 | - $root = stream_resolve_include_path($root); |
|
70 | - $this->validRoots[$root] = true; |
|
71 | - } |
|
72 | - |
|
73 | - /** |
|
74 | - * disable the usage of the global classpath \OC::$CLASSPATH |
|
75 | - */ |
|
76 | - public function disableGlobalClassPath() { |
|
77 | - $this->useGlobalClassPath = false; |
|
78 | - } |
|
79 | - |
|
80 | - /** |
|
81 | - * enable the usage of the global classpath \OC::$CLASSPATH |
|
82 | - */ |
|
83 | - public function enableGlobalClassPath() { |
|
84 | - $this->useGlobalClassPath = true; |
|
85 | - } |
|
86 | - |
|
87 | - /** |
|
88 | - * get the possible paths for a class |
|
89 | - * |
|
90 | - * @param string $class |
|
91 | - * @return array an array of possible paths |
|
92 | - */ |
|
93 | - public function findClass(string $class): array { |
|
94 | - $class = trim($class, '\\'); |
|
95 | - |
|
96 | - $paths = []; |
|
97 | - if ($this->useGlobalClassPath && array_key_exists($class, \OC::$CLASSPATH)) { |
|
98 | - $paths[] = \OC::$CLASSPATH[$class]; |
|
99 | - /** |
|
100 | - * @TODO: Remove this when necessary |
|
101 | - * Remove "apps/" from inclusion path for smooth migration to multi app dir |
|
102 | - */ |
|
103 | - if (strpos(\OC::$CLASSPATH[$class], 'apps/') === 0) { |
|
104 | - \OCP\Util::writeLog('core', 'include path for class "' . $class . '" starts with "apps/"', ILogger::DEBUG); |
|
105 | - $paths[] = str_replace('apps/', '', \OC::$CLASSPATH[$class]); |
|
106 | - } |
|
107 | - } elseif (strpos($class, 'OC_') === 0) { |
|
108 | - $paths[] = \OC::$SERVERROOT . '/lib/private/legacy/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php'); |
|
109 | - } elseif (strpos($class, 'OCA\\') === 0) { |
|
110 | - list(, $app, $rest) = explode('\\', $class, 3); |
|
111 | - $app = strtolower($app); |
|
112 | - $appPath = \OC_App::getAppPath($app); |
|
113 | - if ($appPath && stream_resolve_include_path($appPath)) { |
|
114 | - $paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
115 | - // If not found in the root of the app directory, insert '/lib' after app id and try again. |
|
116 | - $paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
117 | - } |
|
118 | - } elseif ($class === 'Test\\TestCase') { |
|
119 | - // This File is considered public API, so we make sure that the class |
|
120 | - // can still be loaded, although the PSR-4 paths have not been loaded. |
|
121 | - $paths[] = \OC::$SERVERROOT . '/tests/lib/TestCase.php'; |
|
122 | - } |
|
123 | - return $paths; |
|
124 | - } |
|
125 | - |
|
126 | - /** |
|
127 | - * @param string $fullPath |
|
128 | - * @return bool |
|
129 | - * @throws AutoloadNotAllowedException |
|
130 | - */ |
|
131 | - protected function isValidPath(string $fullPath): bool { |
|
132 | - foreach ($this->validRoots as $root => $true) { |
|
133 | - if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') { |
|
134 | - return true; |
|
135 | - } |
|
136 | - } |
|
137 | - throw new AutoloadNotAllowedException($fullPath); |
|
138 | - } |
|
139 | - |
|
140 | - /** |
|
141 | - * Load the specified class |
|
142 | - * |
|
143 | - * @param string $class |
|
144 | - * @return bool |
|
145 | - * @throws AutoloadNotAllowedException |
|
146 | - */ |
|
147 | - public function load(string $class): bool { |
|
148 | - $pathsToRequire = null; |
|
149 | - if ($this->memoryCache) { |
|
150 | - $pathsToRequire = $this->memoryCache->get($class); |
|
151 | - } |
|
152 | - |
|
153 | - if(class_exists($class, false)) { |
|
154 | - return false; |
|
155 | - } |
|
156 | - |
|
157 | - if (!is_array($pathsToRequire)) { |
|
158 | - // No cache or cache miss |
|
159 | - $pathsToRequire = array(); |
|
160 | - foreach ($this->findClass($class) as $path) { |
|
161 | - $fullPath = stream_resolve_include_path($path); |
|
162 | - if ($fullPath && $this->isValidPath($fullPath)) { |
|
163 | - $pathsToRequire[] = $fullPath; |
|
164 | - } |
|
165 | - } |
|
166 | - |
|
167 | - if ($this->memoryCache) { |
|
168 | - $this->memoryCache->set($class, $pathsToRequire, 60); // cache 60 sec |
|
169 | - } |
|
170 | - } |
|
171 | - |
|
172 | - foreach ($pathsToRequire as $fullPath) { |
|
173 | - require_once $fullPath; |
|
174 | - } |
|
175 | - |
|
176 | - return false; |
|
177 | - } |
|
178 | - |
|
179 | - /** |
|
180 | - * Sets the optional low-latency cache for class to path mapping. |
|
181 | - * |
|
182 | - * @param \OC\Memcache\Cache $memoryCache Instance of memory cache. |
|
183 | - */ |
|
184 | - public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null) { |
|
185 | - $this->memoryCache = $memoryCache; |
|
186 | - } |
|
40 | + /** @var bool */ |
|
41 | + private $useGlobalClassPath = true; |
|
42 | + /** @var array */ |
|
43 | + private $validRoots = []; |
|
44 | + |
|
45 | + /** |
|
46 | + * Optional low-latency memory cache for class to path mapping. |
|
47 | + * |
|
48 | + * @var \OC\Memcache\Cache |
|
49 | + */ |
|
50 | + protected $memoryCache; |
|
51 | + |
|
52 | + /** |
|
53 | + * Autoloader constructor. |
|
54 | + * |
|
55 | + * @param string[] $validRoots |
|
56 | + */ |
|
57 | + public function __construct(array $validRoots) { |
|
58 | + foreach ($validRoots as $root) { |
|
59 | + $this->validRoots[$root] = true; |
|
60 | + } |
|
61 | + } |
|
62 | + |
|
63 | + /** |
|
64 | + * Add a path to the list of valid php roots for auto loading |
|
65 | + * |
|
66 | + * @param string $root |
|
67 | + */ |
|
68 | + public function addValidRoot(string $root) { |
|
69 | + $root = stream_resolve_include_path($root); |
|
70 | + $this->validRoots[$root] = true; |
|
71 | + } |
|
72 | + |
|
73 | + /** |
|
74 | + * disable the usage of the global classpath \OC::$CLASSPATH |
|
75 | + */ |
|
76 | + public function disableGlobalClassPath() { |
|
77 | + $this->useGlobalClassPath = false; |
|
78 | + } |
|
79 | + |
|
80 | + /** |
|
81 | + * enable the usage of the global classpath \OC::$CLASSPATH |
|
82 | + */ |
|
83 | + public function enableGlobalClassPath() { |
|
84 | + $this->useGlobalClassPath = true; |
|
85 | + } |
|
86 | + |
|
87 | + /** |
|
88 | + * get the possible paths for a class |
|
89 | + * |
|
90 | + * @param string $class |
|
91 | + * @return array an array of possible paths |
|
92 | + */ |
|
93 | + public function findClass(string $class): array { |
|
94 | + $class = trim($class, '\\'); |
|
95 | + |
|
96 | + $paths = []; |
|
97 | + if ($this->useGlobalClassPath && array_key_exists($class, \OC::$CLASSPATH)) { |
|
98 | + $paths[] = \OC::$CLASSPATH[$class]; |
|
99 | + /** |
|
100 | + * @TODO: Remove this when necessary |
|
101 | + * Remove "apps/" from inclusion path for smooth migration to multi app dir |
|
102 | + */ |
|
103 | + if (strpos(\OC::$CLASSPATH[$class], 'apps/') === 0) { |
|
104 | + \OCP\Util::writeLog('core', 'include path for class "' . $class . '" starts with "apps/"', ILogger::DEBUG); |
|
105 | + $paths[] = str_replace('apps/', '', \OC::$CLASSPATH[$class]); |
|
106 | + } |
|
107 | + } elseif (strpos($class, 'OC_') === 0) { |
|
108 | + $paths[] = \OC::$SERVERROOT . '/lib/private/legacy/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php'); |
|
109 | + } elseif (strpos($class, 'OCA\\') === 0) { |
|
110 | + list(, $app, $rest) = explode('\\', $class, 3); |
|
111 | + $app = strtolower($app); |
|
112 | + $appPath = \OC_App::getAppPath($app); |
|
113 | + if ($appPath && stream_resolve_include_path($appPath)) { |
|
114 | + $paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
115 | + // If not found in the root of the app directory, insert '/lib' after app id and try again. |
|
116 | + $paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
117 | + } |
|
118 | + } elseif ($class === 'Test\\TestCase') { |
|
119 | + // This File is considered public API, so we make sure that the class |
|
120 | + // can still be loaded, although the PSR-4 paths have not been loaded. |
|
121 | + $paths[] = \OC::$SERVERROOT . '/tests/lib/TestCase.php'; |
|
122 | + } |
|
123 | + return $paths; |
|
124 | + } |
|
125 | + |
|
126 | + /** |
|
127 | + * @param string $fullPath |
|
128 | + * @return bool |
|
129 | + * @throws AutoloadNotAllowedException |
|
130 | + */ |
|
131 | + protected function isValidPath(string $fullPath): bool { |
|
132 | + foreach ($this->validRoots as $root => $true) { |
|
133 | + if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') { |
|
134 | + return true; |
|
135 | + } |
|
136 | + } |
|
137 | + throw new AutoloadNotAllowedException($fullPath); |
|
138 | + } |
|
139 | + |
|
140 | + /** |
|
141 | + * Load the specified class |
|
142 | + * |
|
143 | + * @param string $class |
|
144 | + * @return bool |
|
145 | + * @throws AutoloadNotAllowedException |
|
146 | + */ |
|
147 | + public function load(string $class): bool { |
|
148 | + $pathsToRequire = null; |
|
149 | + if ($this->memoryCache) { |
|
150 | + $pathsToRequire = $this->memoryCache->get($class); |
|
151 | + } |
|
152 | + |
|
153 | + if(class_exists($class, false)) { |
|
154 | + return false; |
|
155 | + } |
|
156 | + |
|
157 | + if (!is_array($pathsToRequire)) { |
|
158 | + // No cache or cache miss |
|
159 | + $pathsToRequire = array(); |
|
160 | + foreach ($this->findClass($class) as $path) { |
|
161 | + $fullPath = stream_resolve_include_path($path); |
|
162 | + if ($fullPath && $this->isValidPath($fullPath)) { |
|
163 | + $pathsToRequire[] = $fullPath; |
|
164 | + } |
|
165 | + } |
|
166 | + |
|
167 | + if ($this->memoryCache) { |
|
168 | + $this->memoryCache->set($class, $pathsToRequire, 60); // cache 60 sec |
|
169 | + } |
|
170 | + } |
|
171 | + |
|
172 | + foreach ($pathsToRequire as $fullPath) { |
|
173 | + require_once $fullPath; |
|
174 | + } |
|
175 | + |
|
176 | + return false; |
|
177 | + } |
|
178 | + |
|
179 | + /** |
|
180 | + * Sets the optional low-latency cache for class to path mapping. |
|
181 | + * |
|
182 | + * @param \OC\Memcache\Cache $memoryCache Instance of memory cache. |
|
183 | + */ |
|
184 | + public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null) { |
|
185 | + $this->memoryCache = $memoryCache; |
|
186 | + } |
|
187 | 187 | } |
@@ -101,24 +101,24 @@ discard block |
||
101 | 101 | * Remove "apps/" from inclusion path for smooth migration to multi app dir |
102 | 102 | */ |
103 | 103 | if (strpos(\OC::$CLASSPATH[$class], 'apps/') === 0) { |
104 | - \OCP\Util::writeLog('core', 'include path for class "' . $class . '" starts with "apps/"', ILogger::DEBUG); |
|
104 | + \OCP\Util::writeLog('core', 'include path for class "'.$class.'" starts with "apps/"', ILogger::DEBUG); |
|
105 | 105 | $paths[] = str_replace('apps/', '', \OC::$CLASSPATH[$class]); |
106 | 106 | } |
107 | 107 | } elseif (strpos($class, 'OC_') === 0) { |
108 | - $paths[] = \OC::$SERVERROOT . '/lib/private/legacy/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php'); |
|
108 | + $paths[] = \OC::$SERVERROOT.'/lib/private/legacy/'.strtolower(str_replace('_', '/', substr($class, 3)).'.php'); |
|
109 | 109 | } elseif (strpos($class, 'OCA\\') === 0) { |
110 | 110 | list(, $app, $rest) = explode('\\', $class, 3); |
111 | 111 | $app = strtolower($app); |
112 | 112 | $appPath = \OC_App::getAppPath($app); |
113 | 113 | if ($appPath && stream_resolve_include_path($appPath)) { |
114 | - $paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
114 | + $paths[] = $appPath.'/'.strtolower(str_replace('\\', '/', $rest).'.php'); |
|
115 | 115 | // If not found in the root of the app directory, insert '/lib' after app id and try again. |
116 | - $paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php'); |
|
116 | + $paths[] = $appPath.'/lib/'.strtolower(str_replace('\\', '/', $rest).'.php'); |
|
117 | 117 | } |
118 | 118 | } elseif ($class === 'Test\\TestCase') { |
119 | 119 | // This File is considered public API, so we make sure that the class |
120 | 120 | // can still be loaded, although the PSR-4 paths have not been loaded. |
121 | - $paths[] = \OC::$SERVERROOT . '/tests/lib/TestCase.php'; |
|
121 | + $paths[] = \OC::$SERVERROOT.'/tests/lib/TestCase.php'; |
|
122 | 122 | } |
123 | 123 | return $paths; |
124 | 124 | } |
@@ -130,7 +130,7 @@ discard block |
||
130 | 130 | */ |
131 | 131 | protected function isValidPath(string $fullPath): bool { |
132 | 132 | foreach ($this->validRoots as $root => $true) { |
133 | - if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') { |
|
133 | + if (substr($fullPath, 0, strlen($root) + 1) === $root.'/') { |
|
134 | 134 | return true; |
135 | 135 | } |
136 | 136 | } |
@@ -150,7 +150,7 @@ discard block |
||
150 | 150 | $pathsToRequire = $this->memoryCache->get($class); |
151 | 151 | } |
152 | 152 | |
153 | - if(class_exists($class, false)) { |
|
153 | + if (class_exists($class, false)) { |
|
154 | 154 | return false; |
155 | 155 | } |
156 | 156 |