Passed
Push — master ( d06716...a6bc87 )
by John
25:35 queued 09:59
created
apps/files_sharing/lib/External/Storage.php 1 patch
Indentation   +377 added lines, -377 removed lines patch added patch discarded remove patch
@@ -49,381 +49,381 @@
 block discarded – undo
49 49
 use OCP\Http\Client\IClientService;
50 50
 
51 51
 class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage {
52
-	/** @var ICloudId */
53
-	private $cloudId;
54
-	/** @var string */
55
-	private $mountPoint;
56
-	/** @var string */
57
-	private $token;
58
-	/** @var \OCP\ICacheFactory */
59
-	private $memcacheFactory;
60
-	/** @var \OCP\Http\Client\IClientService */
61
-	private $httpClient;
62
-	/** @var bool */
63
-	private $updateChecked = false;
64
-
65
-	/** @var ExternalShareManager */
66
-	private $manager;
67
-
68
-	/**
69
-	 * @param array{HttpClientService: IClientService, manager: ExternalShareManager, cloudId: ICloudId, mountpoint: string, token: string, password: ?string}|array $options
70
-	 */
71
-	public function __construct($options) {
72
-		$this->memcacheFactory = \OC::$server->getMemCacheFactory();
73
-		$this->httpClient = $options['HttpClientService'];
74
-
75
-		$this->manager = $options['manager'];
76
-		$this->cloudId = $options['cloudId'];
77
-		$discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
78
-
79
-		[$protocol, $remote] = explode('://', $this->cloudId->getRemote());
80
-		if (strpos($remote, '/')) {
81
-			[$host, $root] = explode('/', $remote, 2);
82
-		} else {
83
-			$host = $remote;
84
-			$root = '';
85
-		}
86
-		$secure = $protocol === 'https';
87
-		$federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING');
88
-		$webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav';
89
-		$root = rtrim($root, '/') . $webDavEndpoint;
90
-		$this->mountPoint = $options['mountpoint'];
91
-		$this->token = $options['token'];
92
-
93
-		parent::__construct([
94
-			'secure' => $secure,
95
-			'host' => $host,
96
-			'root' => $root,
97
-			'user' => $options['token'],
98
-			'password' => (string)$options['password']
99
-		]);
100
-	}
101
-
102
-	public function getWatcher($path = '', $storage = null) {
103
-		if (!$storage) {
104
-			$storage = $this;
105
-		}
106
-		if (!isset($this->watcher)) {
107
-			$this->watcher = new Watcher($storage);
108
-			$this->watcher->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE);
109
-		}
110
-		return $this->watcher;
111
-	}
112
-
113
-	public function getRemoteUser(): string {
114
-		return $this->cloudId->getUser();
115
-	}
116
-
117
-	public function getRemote(): string {
118
-		return $this->cloudId->getRemote();
119
-	}
120
-
121
-	public function getMountPoint(): string {
122
-		return $this->mountPoint;
123
-	}
124
-
125
-	public function getToken(): string {
126
-		return $this->token;
127
-	}
128
-
129
-	public function getPassword(): ?string {
130
-		return $this->password;
131
-	}
132
-
133
-	/**
134
-	 * Get id of the mount point.
135
-	 * @return string
136
-	 */
137
-	public function getId() {
138
-		return 'shared::' . md5($this->token . '@' . $this->getRemote());
139
-	}
140
-
141
-	public function getCache($path = '', $storage = null) {
142
-		if (is_null($this->cache)) {
143
-			$this->cache = new Cache($this, $this->cloudId);
144
-		}
145
-		return $this->cache;
146
-	}
147
-
148
-	/**
149
-	 * @param string $path
150
-	 * @param \OC\Files\Storage\Storage $storage
151
-	 * @return \OCA\Files_Sharing\External\Scanner
152
-	 */
153
-	public function getScanner($path = '', $storage = null) {
154
-		if (!$storage) {
155
-			$storage = $this;
156
-		}
157
-		if (!isset($this->scanner)) {
158
-			$this->scanner = new Scanner($storage);
159
-		}
160
-		return $this->scanner;
161
-	}
162
-
163
-	/**
164
-	 * Check if a file or folder has been updated since $time
165
-	 *
166
-	 * @param string $path
167
-	 * @param int $time
168
-	 * @throws \OCP\Files\StorageNotAvailableException
169
-	 * @throws \OCP\Files\StorageInvalidException
170
-	 * @return bool
171
-	 */
172
-	public function hasUpdated($path, $time) {
173
-		// since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage
174
-		// because of that we only do one check for the entire storage per request
175
-		if ($this->updateChecked) {
176
-			return false;
177
-		}
178
-		$this->updateChecked = true;
179
-		try {
180
-			return parent::hasUpdated('', $time);
181
-		} catch (StorageInvalidException $e) {
182
-			// check if it needs to be removed
183
-			$this->checkStorageAvailability();
184
-			throw $e;
185
-		} catch (StorageNotAvailableException $e) {
186
-			// check if it needs to be removed or just temp unavailable
187
-			$this->checkStorageAvailability();
188
-			throw $e;
189
-		}
190
-	}
191
-
192
-	public function test() {
193
-		try {
194
-			return parent::test();
195
-		} catch (StorageInvalidException $e) {
196
-			// check if it needs to be removed
197
-			$this->checkStorageAvailability();
198
-			throw $e;
199
-		} catch (StorageNotAvailableException $e) {
200
-			// check if it needs to be removed or just temp unavailable
201
-			$this->checkStorageAvailability();
202
-			throw $e;
203
-		}
204
-	}
205
-
206
-	/**
207
-	 * Check whether this storage is permanently or temporarily
208
-	 * unavailable
209
-	 *
210
-	 * @throws \OCP\Files\StorageNotAvailableException
211
-	 * @throws \OCP\Files\StorageInvalidException
212
-	 */
213
-	public function checkStorageAvailability() {
214
-		// see if we can find out why the share is unavailable
215
-		try {
216
-			$this->getShareInfo();
217
-		} catch (NotFoundException $e) {
218
-			// a 404 can either mean that the share no longer exists or there is no Nextcloud on the remote
219
-			if ($this->testRemote()) {
220
-				// valid Nextcloud instance means that the public share no longer exists
221
-				// since this is permanent (re-sharing the file will create a new token)
222
-				// we remove the invalid storage
223
-				$this->manager->removeShare($this->mountPoint);
224
-				$this->manager->getMountManager()->removeMount($this->mountPoint);
225
-				throw new StorageInvalidException("Remote share not found", 0, $e);
226
-			} else {
227
-				// Nextcloud instance is gone, likely to be a temporary server configuration error
228
-				throw new StorageNotAvailableException("No nextcloud instance found at remote", 0, $e);
229
-			}
230
-		} catch (ForbiddenException $e) {
231
-			// auth error, remove share for now (provide a dialog in the future)
232
-			$this->manager->removeShare($this->mountPoint);
233
-			$this->manager->getMountManager()->removeMount($this->mountPoint);
234
-			throw new StorageInvalidException("Auth error when getting remote share");
235
-		} catch (\GuzzleHttp\Exception\ConnectException $e) {
236
-			throw new StorageNotAvailableException("Failed to connect to remote instance", 0, $e);
237
-		} catch (\GuzzleHttp\Exception\RequestException $e) {
238
-			throw new StorageNotAvailableException("Error while sending request to remote instance", 0, $e);
239
-		}
240
-	}
241
-
242
-	public function file_exists($path) {
243
-		if ($path === '') {
244
-			return true;
245
-		} else {
246
-			return parent::file_exists($path);
247
-		}
248
-	}
249
-
250
-	/**
251
-	 * Check if the configured remote is a valid federated share provider
252
-	 *
253
-	 * @return bool
254
-	 */
255
-	protected function testRemote(): bool {
256
-		try {
257
-			return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php')
258
-				|| $this->testRemoteUrl($this->getRemote() . '/ocs-provider/')
259
-				|| $this->testRemoteUrl($this->getRemote() . '/status.php');
260
-		} catch (\Exception $e) {
261
-			return false;
262
-		}
263
-	}
264
-
265
-	private function testRemoteUrl(string $url): bool {
266
-		$cache = $this->memcacheFactory->createDistributed('files_sharing_remote_url');
267
-		if ($cache->hasKey($url)) {
268
-			return (bool)$cache->get($url);
269
-		}
270
-
271
-		$client = $this->httpClient->newClient();
272
-		try {
273
-			$result = $client->get($url, [
274
-				'timeout' => 10,
275
-				'connect_timeout' => 10,
276
-			])->getBody();
277
-			$data = json_decode($result);
278
-			$returnValue = (is_object($data) && !empty($data->version));
279
-		} catch (ConnectException $e) {
280
-			$returnValue = false;
281
-		} catch (ClientException $e) {
282
-			$returnValue = false;
283
-		} catch (RequestException $e) {
284
-			$returnValue = false;
285
-		}
286
-
287
-		$cache->set($url, $returnValue, 60 * 60 * 24);
288
-		return $returnValue;
289
-	}
290
-
291
-	/**
292
-	 * Check whether the remote is an ownCloud/Nextcloud. This is needed since some sharing
293
-	 * features are not standardized.
294
-	 *
295
-	 * @throws LocalServerException
296
-	 */
297
-	public function remoteIsOwnCloud(): bool {
298
-		if (defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote() . '/status.php')) {
299
-			return false;
300
-		}
301
-		return true;
302
-	}
303
-
304
-	/**
305
-	 * @return mixed
306
-	 * @throws ForbiddenException
307
-	 * @throws NotFoundException
308
-	 * @throws \Exception
309
-	 */
310
-	public function getShareInfo() {
311
-		$remote = $this->getRemote();
312
-		$token = $this->getToken();
313
-		$password = $this->getPassword();
314
-
315
-		try {
316
-			// If remote is not an ownCloud do not try to get any share info
317
-			if (!$this->remoteIsOwnCloud()) {
318
-				return ['status' => 'unsupported'];
319
-			}
320
-		} catch (LocalServerException $e) {
321
-			// throw this to be on the safe side: the share will still be visible
322
-			// in the UI in case the failure is intermittent, and the user will
323
-			// be able to decide whether to remove it if it's really gone
324
-			throw new StorageNotAvailableException();
325
-		}
326
-
327
-		$url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
328
-
329
-		// TODO: DI
330
-		$client = \OC::$server->getHTTPClientService()->newClient();
331
-		try {
332
-			$response = $client->post($url, [
333
-				'body' => ['password' => $password],
334
-				'timeout' => 10,
335
-				'connect_timeout' => 10,
336
-			]);
337
-		} catch (\GuzzleHttp\Exception\RequestException $e) {
338
-			if ($e->getCode() === Http::STATUS_UNAUTHORIZED || $e->getCode() === Http::STATUS_FORBIDDEN) {
339
-				throw new ForbiddenException();
340
-			}
341
-			if ($e->getCode() === Http::STATUS_NOT_FOUND) {
342
-				throw new NotFoundException();
343
-			}
344
-			// throw this to be on the safe side: the share will still be visible
345
-			// in the UI in case the failure is intermittent, and the user will
346
-			// be able to decide whether to remove it if it's really gone
347
-			throw new StorageNotAvailableException();
348
-		}
349
-
350
-		return json_decode($response->getBody(), true);
351
-	}
352
-
353
-	public function getOwner($path) {
354
-		return $this->cloudId->getDisplayId();
355
-	}
356
-
357
-	public function isSharable($path) {
358
-		if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
359
-			return false;
360
-		}
361
-		return ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
362
-	}
363
-
364
-	public function getPermissions($path) {
365
-		$response = $this->propfind($path);
366
-		// old federated sharing permissions
367
-		if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
368
-			$permissions = $response['{http://open-collaboration-services.org/ns}share-permissions'];
369
-		} elseif (isset($response['{http://open-cloud-mesh.org/ns}share-permissions'])) {
370
-			// permissions provided by the OCM API
371
-			$permissions = $this->ocmPermissions2ncPermissions($response['{http://open-collaboration-services.org/ns}share-permissions'], $path);
372
-		} else {
373
-			// use default permission if remote server doesn't provide the share permissions
374
-			$permissions = $this->getDefaultPermissions($path);
375
-		}
376
-
377
-		return $permissions;
378
-	}
379
-
380
-	public function needsPartFile() {
381
-		return false;
382
-	}
383
-
384
-	/**
385
-	 * Translate OCM Permissions to Nextcloud permissions
386
-	 *
387
-	 * @param string $ocmPermissions json encoded OCM permissions
388
-	 * @param string $path path to file
389
-	 * @return int
390
-	 */
391
-	protected function ocmPermissions2ncPermissions(string $ocmPermissions, string $path): int {
392
-		try {
393
-			$ocmPermissions = json_decode($ocmPermissions);
394
-			$ncPermissions = 0;
395
-			foreach ($ocmPermissions as $permission) {
396
-				switch (strtolower($permission)) {
397
-					case 'read':
398
-						$ncPermissions += Constants::PERMISSION_READ;
399
-						break;
400
-					case 'write':
401
-						$ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
402
-						break;
403
-					case 'share':
404
-						$ncPermissions += Constants::PERMISSION_SHARE;
405
-						break;
406
-					default:
407
-						throw new \Exception();
408
-				}
409
-			}
410
-		} catch (\Exception $e) {
411
-			$ncPermissions = $this->getDefaultPermissions($path);
412
-		}
413
-
414
-		return $ncPermissions;
415
-	}
416
-
417
-	/**
418
-	 * Calculate the default permissions in case no permissions are provided
419
-	 */
420
-	protected function getDefaultPermissions(string $path): int {
421
-		if ($this->is_dir($path)) {
422
-			$permissions = Constants::PERMISSION_ALL;
423
-		} else {
424
-			$permissions = Constants::PERMISSION_ALL & ~Constants::PERMISSION_CREATE;
425
-		}
426
-
427
-		return $permissions;
428
-	}
52
+    /** @var ICloudId */
53
+    private $cloudId;
54
+    /** @var string */
55
+    private $mountPoint;
56
+    /** @var string */
57
+    private $token;
58
+    /** @var \OCP\ICacheFactory */
59
+    private $memcacheFactory;
60
+    /** @var \OCP\Http\Client\IClientService */
61
+    private $httpClient;
62
+    /** @var bool */
63
+    private $updateChecked = false;
64
+
65
+    /** @var ExternalShareManager */
66
+    private $manager;
67
+
68
+    /**
69
+     * @param array{HttpClientService: IClientService, manager: ExternalShareManager, cloudId: ICloudId, mountpoint: string, token: string, password: ?string}|array $options
70
+     */
71
+    public function __construct($options) {
72
+        $this->memcacheFactory = \OC::$server->getMemCacheFactory();
73
+        $this->httpClient = $options['HttpClientService'];
74
+
75
+        $this->manager = $options['manager'];
76
+        $this->cloudId = $options['cloudId'];
77
+        $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class);
78
+
79
+        [$protocol, $remote] = explode('://', $this->cloudId->getRemote());
80
+        if (strpos($remote, '/')) {
81
+            [$host, $root] = explode('/', $remote, 2);
82
+        } else {
83
+            $host = $remote;
84
+            $root = '';
85
+        }
86
+        $secure = $protocol === 'https';
87
+        $federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING');
88
+        $webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav';
89
+        $root = rtrim($root, '/') . $webDavEndpoint;
90
+        $this->mountPoint = $options['mountpoint'];
91
+        $this->token = $options['token'];
92
+
93
+        parent::__construct([
94
+            'secure' => $secure,
95
+            'host' => $host,
96
+            'root' => $root,
97
+            'user' => $options['token'],
98
+            'password' => (string)$options['password']
99
+        ]);
100
+    }
101
+
102
+    public function getWatcher($path = '', $storage = null) {
103
+        if (!$storage) {
104
+            $storage = $this;
105
+        }
106
+        if (!isset($this->watcher)) {
107
+            $this->watcher = new Watcher($storage);
108
+            $this->watcher->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE);
109
+        }
110
+        return $this->watcher;
111
+    }
112
+
113
+    public function getRemoteUser(): string {
114
+        return $this->cloudId->getUser();
115
+    }
116
+
117
+    public function getRemote(): string {
118
+        return $this->cloudId->getRemote();
119
+    }
120
+
121
+    public function getMountPoint(): string {
122
+        return $this->mountPoint;
123
+    }
124
+
125
+    public function getToken(): string {
126
+        return $this->token;
127
+    }
128
+
129
+    public function getPassword(): ?string {
130
+        return $this->password;
131
+    }
132
+
133
+    /**
134
+     * Get id of the mount point.
135
+     * @return string
136
+     */
137
+    public function getId() {
138
+        return 'shared::' . md5($this->token . '@' . $this->getRemote());
139
+    }
140
+
141
+    public function getCache($path = '', $storage = null) {
142
+        if (is_null($this->cache)) {
143
+            $this->cache = new Cache($this, $this->cloudId);
144
+        }
145
+        return $this->cache;
146
+    }
147
+
148
+    /**
149
+     * @param string $path
150
+     * @param \OC\Files\Storage\Storage $storage
151
+     * @return \OCA\Files_Sharing\External\Scanner
152
+     */
153
+    public function getScanner($path = '', $storage = null) {
154
+        if (!$storage) {
155
+            $storage = $this;
156
+        }
157
+        if (!isset($this->scanner)) {
158
+            $this->scanner = new Scanner($storage);
159
+        }
160
+        return $this->scanner;
161
+    }
162
+
163
+    /**
164
+     * Check if a file or folder has been updated since $time
165
+     *
166
+     * @param string $path
167
+     * @param int $time
168
+     * @throws \OCP\Files\StorageNotAvailableException
169
+     * @throws \OCP\Files\StorageInvalidException
170
+     * @return bool
171
+     */
172
+    public function hasUpdated($path, $time) {
173
+        // since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage
174
+        // because of that we only do one check for the entire storage per request
175
+        if ($this->updateChecked) {
176
+            return false;
177
+        }
178
+        $this->updateChecked = true;
179
+        try {
180
+            return parent::hasUpdated('', $time);
181
+        } catch (StorageInvalidException $e) {
182
+            // check if it needs to be removed
183
+            $this->checkStorageAvailability();
184
+            throw $e;
185
+        } catch (StorageNotAvailableException $e) {
186
+            // check if it needs to be removed or just temp unavailable
187
+            $this->checkStorageAvailability();
188
+            throw $e;
189
+        }
190
+    }
191
+
192
+    public function test() {
193
+        try {
194
+            return parent::test();
195
+        } catch (StorageInvalidException $e) {
196
+            // check if it needs to be removed
197
+            $this->checkStorageAvailability();
198
+            throw $e;
199
+        } catch (StorageNotAvailableException $e) {
200
+            // check if it needs to be removed or just temp unavailable
201
+            $this->checkStorageAvailability();
202
+            throw $e;
203
+        }
204
+    }
205
+
206
+    /**
207
+     * Check whether this storage is permanently or temporarily
208
+     * unavailable
209
+     *
210
+     * @throws \OCP\Files\StorageNotAvailableException
211
+     * @throws \OCP\Files\StorageInvalidException
212
+     */
213
+    public function checkStorageAvailability() {
214
+        // see if we can find out why the share is unavailable
215
+        try {
216
+            $this->getShareInfo();
217
+        } catch (NotFoundException $e) {
218
+            // a 404 can either mean that the share no longer exists or there is no Nextcloud on the remote
219
+            if ($this->testRemote()) {
220
+                // valid Nextcloud instance means that the public share no longer exists
221
+                // since this is permanent (re-sharing the file will create a new token)
222
+                // we remove the invalid storage
223
+                $this->manager->removeShare($this->mountPoint);
224
+                $this->manager->getMountManager()->removeMount($this->mountPoint);
225
+                throw new StorageInvalidException("Remote share not found", 0, $e);
226
+            } else {
227
+                // Nextcloud instance is gone, likely to be a temporary server configuration error
228
+                throw new StorageNotAvailableException("No nextcloud instance found at remote", 0, $e);
229
+            }
230
+        } catch (ForbiddenException $e) {
231
+            // auth error, remove share for now (provide a dialog in the future)
232
+            $this->manager->removeShare($this->mountPoint);
233
+            $this->manager->getMountManager()->removeMount($this->mountPoint);
234
+            throw new StorageInvalidException("Auth error when getting remote share");
235
+        } catch (\GuzzleHttp\Exception\ConnectException $e) {
236
+            throw new StorageNotAvailableException("Failed to connect to remote instance", 0, $e);
237
+        } catch (\GuzzleHttp\Exception\RequestException $e) {
238
+            throw new StorageNotAvailableException("Error while sending request to remote instance", 0, $e);
239
+        }
240
+    }
241
+
242
+    public function file_exists($path) {
243
+        if ($path === '') {
244
+            return true;
245
+        } else {
246
+            return parent::file_exists($path);
247
+        }
248
+    }
249
+
250
+    /**
251
+     * Check if the configured remote is a valid federated share provider
252
+     *
253
+     * @return bool
254
+     */
255
+    protected function testRemote(): bool {
256
+        try {
257
+            return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php')
258
+                || $this->testRemoteUrl($this->getRemote() . '/ocs-provider/')
259
+                || $this->testRemoteUrl($this->getRemote() . '/status.php');
260
+        } catch (\Exception $e) {
261
+            return false;
262
+        }
263
+    }
264
+
265
+    private function testRemoteUrl(string $url): bool {
266
+        $cache = $this->memcacheFactory->createDistributed('files_sharing_remote_url');
267
+        if ($cache->hasKey($url)) {
268
+            return (bool)$cache->get($url);
269
+        }
270
+
271
+        $client = $this->httpClient->newClient();
272
+        try {
273
+            $result = $client->get($url, [
274
+                'timeout' => 10,
275
+                'connect_timeout' => 10,
276
+            ])->getBody();
277
+            $data = json_decode($result);
278
+            $returnValue = (is_object($data) && !empty($data->version));
279
+        } catch (ConnectException $e) {
280
+            $returnValue = false;
281
+        } catch (ClientException $e) {
282
+            $returnValue = false;
283
+        } catch (RequestException $e) {
284
+            $returnValue = false;
285
+        }
286
+
287
+        $cache->set($url, $returnValue, 60 * 60 * 24);
288
+        return $returnValue;
289
+    }
290
+
291
+    /**
292
+     * Check whether the remote is an ownCloud/Nextcloud. This is needed since some sharing
293
+     * features are not standardized.
294
+     *
295
+     * @throws LocalServerException
296
+     */
297
+    public function remoteIsOwnCloud(): bool {
298
+        if (defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote() . '/status.php')) {
299
+            return false;
300
+        }
301
+        return true;
302
+    }
303
+
304
+    /**
305
+     * @return mixed
306
+     * @throws ForbiddenException
307
+     * @throws NotFoundException
308
+     * @throws \Exception
309
+     */
310
+    public function getShareInfo() {
311
+        $remote = $this->getRemote();
312
+        $token = $this->getToken();
313
+        $password = $this->getPassword();
314
+
315
+        try {
316
+            // If remote is not an ownCloud do not try to get any share info
317
+            if (!$this->remoteIsOwnCloud()) {
318
+                return ['status' => 'unsupported'];
319
+            }
320
+        } catch (LocalServerException $e) {
321
+            // throw this to be on the safe side: the share will still be visible
322
+            // in the UI in case the failure is intermittent, and the user will
323
+            // be able to decide whether to remove it if it's really gone
324
+            throw new StorageNotAvailableException();
325
+        }
326
+
327
+        $url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
328
+
329
+        // TODO: DI
330
+        $client = \OC::$server->getHTTPClientService()->newClient();
331
+        try {
332
+            $response = $client->post($url, [
333
+                'body' => ['password' => $password],
334
+                'timeout' => 10,
335
+                'connect_timeout' => 10,
336
+            ]);
337
+        } catch (\GuzzleHttp\Exception\RequestException $e) {
338
+            if ($e->getCode() === Http::STATUS_UNAUTHORIZED || $e->getCode() === Http::STATUS_FORBIDDEN) {
339
+                throw new ForbiddenException();
340
+            }
341
+            if ($e->getCode() === Http::STATUS_NOT_FOUND) {
342
+                throw new NotFoundException();
343
+            }
344
+            // throw this to be on the safe side: the share will still be visible
345
+            // in the UI in case the failure is intermittent, and the user will
346
+            // be able to decide whether to remove it if it's really gone
347
+            throw new StorageNotAvailableException();
348
+        }
349
+
350
+        return json_decode($response->getBody(), true);
351
+    }
352
+
353
+    public function getOwner($path) {
354
+        return $this->cloudId->getDisplayId();
355
+    }
356
+
357
+    public function isSharable($path) {
358
+        if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
359
+            return false;
360
+        }
361
+        return ($this->getPermissions($path) & Constants::PERMISSION_SHARE);
362
+    }
363
+
364
+    public function getPermissions($path) {
365
+        $response = $this->propfind($path);
366
+        // old federated sharing permissions
367
+        if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
368
+            $permissions = $response['{http://open-collaboration-services.org/ns}share-permissions'];
369
+        } elseif (isset($response['{http://open-cloud-mesh.org/ns}share-permissions'])) {
370
+            // permissions provided by the OCM API
371
+            $permissions = $this->ocmPermissions2ncPermissions($response['{http://open-collaboration-services.org/ns}share-permissions'], $path);
372
+        } else {
373
+            // use default permission if remote server doesn't provide the share permissions
374
+            $permissions = $this->getDefaultPermissions($path);
375
+        }
376
+
377
+        return $permissions;
378
+    }
379
+
380
+    public function needsPartFile() {
381
+        return false;
382
+    }
383
+
384
+    /**
385
+     * Translate OCM Permissions to Nextcloud permissions
386
+     *
387
+     * @param string $ocmPermissions json encoded OCM permissions
388
+     * @param string $path path to file
389
+     * @return int
390
+     */
391
+    protected function ocmPermissions2ncPermissions(string $ocmPermissions, string $path): int {
392
+        try {
393
+            $ocmPermissions = json_decode($ocmPermissions);
394
+            $ncPermissions = 0;
395
+            foreach ($ocmPermissions as $permission) {
396
+                switch (strtolower($permission)) {
397
+                    case 'read':
398
+                        $ncPermissions += Constants::PERMISSION_READ;
399
+                        break;
400
+                    case 'write':
401
+                        $ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
402
+                        break;
403
+                    case 'share':
404
+                        $ncPermissions += Constants::PERMISSION_SHARE;
405
+                        break;
406
+                    default:
407
+                        throw new \Exception();
408
+                }
409
+            }
410
+        } catch (\Exception $e) {
411
+            $ncPermissions = $this->getDefaultPermissions($path);
412
+        }
413
+
414
+        return $ncPermissions;
415
+    }
416
+
417
+    /**
418
+     * Calculate the default permissions in case no permissions are provided
419
+     */
420
+    protected function getDefaultPermissions(string $path): int {
421
+        if ($this->is_dir($path)) {
422
+            $permissions = Constants::PERMISSION_ALL;
423
+        } else {
424
+            $permissions = Constants::PERMISSION_ALL & ~Constants::PERMISSION_CREATE;
425
+        }
426
+
427
+        return $permissions;
428
+    }
429 429
 }
Please login to merge, or discard this patch.