Completed
Pull Request — master (#32303)
by Victor
16:44 queued 05:42
created

Storage::init()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 18

Duplication

Lines 3
Ratio 16.67 %

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 0
dl 3
loc 18
rs 9.3554
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Lukas Reschke <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Robin Appelman <[email protected]>
8
 * @author Thomas Müller <[email protected]>
9
 * @author Vincent Petry <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2018, ownCloud GmbH
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OCA\Files_Sharing\External;
29
30
use GuzzleHttp\Exception\ClientException;
31
use GuzzleHttp\Exception\ConnectException;
32
use OC\Files\Storage\DAV;
33
use OCA\FederatedFileSharing\DiscoveryManager;
34
use OCA\Files_Sharing\ISharedStorage;
35
use OCP\Files\StorageInvalidException;
36
use OCP\Files\StorageNotAvailableException;
37
use Sabre\DAV\Client;
38
39
class Storage extends DAV implements ISharedStorage {
40
	/** @var string */
41
	private $remoteUser;
42
	/** @var string */
43
	private $remote;
44
	/** @var string */
45
	private $mountPoint;
46
	/** @var string */
47
	private $token;
48
	/** @var \OCP\ICacheFactory */
49
	private $memcacheFactory;
50
	/** @var \OCP\Http\Client\IClientService */
51
	private $httpClient;
52
	/** @var \OCP\ICertificateManager */
53
	private $certificateManager;
54
	/** @var bool */
55
	private $updateChecked = false;
56
57
	/**
58
	 * @var \OCA\Files_Sharing\External\Manager
59
	 */
60
	private $manager;
61
62
	public function __construct($options) {
63
		$this->memcacheFactory = \OC::$server->getMemCacheFactory();
64
		$this->httpClient = \OC::$server->getHTTPClientService();
65
		$this->manager = $options['manager'];
66
		$this->certificateManager = $options['certificateManager'];
67
		$this->remote = $options['remote'];
68
		$this->remoteUser = $options['owner'];
69
		list($protocol, $remote) = \explode('://', $this->remote);
70
		if (\strpos($remote, '/')) {
71
			list($host, $root) = \explode('/', $remote, 2);
72
		} else {
73
			$host = $remote;
74
			$root = '';
75
		}
76
		$secure = $protocol === 'https';
77
		$this->mountPoint = $options['mountpoint'];
78
		$this->token = $options['token'];
79
		parent::__construct([
80
			'secure' => $secure,
81
			'host' => $host,
82
			// root will be adjusted lazily in init() with discovery manager
83
			'root' => $root,
84
			'user' => $options['token'],
85
			'password' => (string)$options['password'],
86
			// Federated sharing always uses BASIC auth
87
			'authType' => Client::AUTH_BASIC
88
		]);
89
	}
90
91
	protected function init() {
92
		if ($this->ready) {
93
			return;
94
		}
95
		$discoveryManager = new DiscoveryManager(
96
			$this->memcacheFactory,
97
			\OC::$server->getHTTPClientService()
98
		);
99
100
		$ocmEndpoint = $discoveryManager->getOcmWebDavEndPoint($this->remote)
101
102
		$this->root = \rtrim($this->root, '/') . $discoveryManager->getWebDavEndpoint($this->remote);
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_VARIABLE
Loading history...
103
		if (!$this->root || $this->root[0] !== '/') {
104
			$this->root = '/' . $this->root;
105
		}
106
		if (\substr($this->root, -1, 1) !== '/') {
107
			$this->root .= '/';
108
		}
109
		parent::init();
110
	}
111
112
	/** {@inheritdoc} */
113
	public function createBaseUri() {
114
		// require lazy-initializing root to return correct value
115
		$this->init();
116
		return parent::createBaseUri();
117
	}
118
119
	public function getWatcher($path = '', $storage = null) {
120
		if (!$storage) {
121
			$storage = $this;
122
		}
123
		if (!isset($this->watcher)) {
124
			$this->watcher = new Watcher($storage);
125
			$this->watcher->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE);
126
		}
127
		return $this->watcher;
128
	}
129
130
	public function getRemoteUser() {
131
		return $this->remoteUser;
132
	}
133
134
	public function getRemote() {
135
		return $this->remote;
136
	}
137
138
	public function getMountPoint() {
139
		return $this->mountPoint;
140
	}
141
142
	public function getToken() {
143
		return $this->token;
144
	}
145
146
	public function getPassword() {
147
		return $this->password;
148
	}
149
150
	/**
151
	 * @brief get id of the mount point
152
	 * @return string
153
	 */
154
	public function getId() {
155
		return 'shared::' . \md5($this->token . '@' . $this->remote);
156
	}
157
158
	public function getCache($path = '', $storage = null) {
159
		if ($this->cache === null) {
160
			$this->cache = new Cache($this, $this->remote, $this->remoteUser);
161
		}
162
		return $this->cache;
163
	}
164
165
	/**
166
	 * check if a file or folder has been updated since $time
167
	 *
168
	 * @param string $path
169
	 * @param int $time
170
	 * @throws \OCP\Files\StorageNotAvailableException
171
	 * @throws \OCP\Files\StorageInvalidException
172
	 * @return bool
173
	 */
174
	public function hasUpdated($path, $time) {
175
		// since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage
176
		// because of that we only do one check for the entire storage per request
177
		if ($this->updateChecked) {
178
			return false;
179
		}
180
		$this->updateChecked = true;
181
		try {
182
			return parent::hasUpdated('', $time);
183
		} catch (StorageInvalidException $e) {
184
			// check if it needs to be removed
185
			$this->checkStorageAvailability();
186
			throw $e;
187
		} catch (StorageNotAvailableException $e) {
188
			// check if it needs to be removed or just temp unavailable
189
			$this->checkStorageAvailability();
190
			throw $e;
191
		}
192
	}
193
194
	public function test() {
195
		try {
196
			return parent::test();
197
		} catch (StorageInvalidException $e) {
198
			// check if it needs to be removed
199
			$this->checkStorageAvailability();
200
			throw $e;
201
		} catch (StorageNotAvailableException $e) {
202
			// check if it needs to be removed or just temp unavailable
203
			$this->checkStorageAvailability();
204
			throw $e;
205
		}
206
	}
207
208
	/**
209
	 * Check whether this storage is permanently or temporarily
210
	 * unavailable
211
	 *
212
	 * @throws \OCP\Files\StorageNotAvailableException
213
	 * @throws \OCP\Files\StorageInvalidException
214
	 */
215
	public function checkStorageAvailability() {
216
		// see if we can find out why the share is unavailable
217
		try {
218
			if (! $this->propfind('')) {
219
				// a 404 can either mean that the share no longer exists or there is no ownCloud on the remote
220
				if ($this->testRemote()) {
221
					// valid ownCloud instance means that the public share no longer exists
222
					// since this is permanent (re-sharing the file will create a new token)
223
					// we remove the invalid storage
224
					$this->manager->removeShare($this->mountPoint);
225
					$this->manager->getMountManager()->removeMount($this->mountPoint);
226
					throw new StorageInvalidException();
227
				} else {
228
					// ownCloud instance is gone, likely to be a temporary server configuration error
229
					throw new StorageNotAvailableException();
230
				}
231
			}
232
		} catch (StorageInvalidException $e) {
233
			// auth error, remove share for now (provide a dialog in the future)
234
			$this->manager->removeShare($this->mountPoint);
235
			$this->manager->getMountManager()->removeMount($this->mountPoint);
236
			throw $e;
237
		} catch (\Exception $e) {
238
			throw $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() {
256
		try {
257
			return $this->testRemoteUrl($this->remote . '/ocs-provider/index.php')
258
				|| $this->testRemoteUrl($this->remote . '/ocs-provider/')
259
				|| $this->testRemoteUrl($this->remote . '/status.php');
260
		} catch (\Exception $e) {
261
			return false;
262
		}
263
	}
264
265
	/**
266
	 * @param string $url
267
	 * @return bool
268
	 */
269
	private function testRemoteUrl($url) {
270
		$cache = $this->memcacheFactory->create('files_sharing_remote_url');
271
		if ($cache->hasKey($url)) {
272
			return (bool)$cache->get($url);
273
		}
274
275
		$client = $this->httpClient->newClient();
276
		try {
277
			$result = $client->get($url, [
278
				'timeout' => 10,
279
				'connect_timeout' => 10,
280
			])->getBody();
281
			$data = \json_decode($result);
282
			$returnValue = (\is_object($data) && !empty($data->version));
283
		} catch (ConnectException $e) {
284
			$returnValue = false;
285
		} catch (ClientException $e) {
286
			$returnValue = false;
287
		}
288
289
		$cache->set($url, $returnValue);
290
		return $returnValue;
291
	}
292
293
	public function getOwner($path) {
294
		list(, $remote) = \explode('://', $this->remote, 2);
295
		return $this->remoteUser . '@' . $remote;
296
	}
297
298
	public function isSharable($path) {
299
		if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) {
300
			return false;
301
		}
302
		return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE);
303
	}
304
	
305
	public function getPermissions($path) {
306
		$response = $this->propfind($path);
307
		if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) {
308
			$permissions = $response['{http://open-collaboration-services.org/ns}share-permissions'];
309
		} else {
310
			// use default permission if remote server doesn't provide the share permissions
311
			if ($this->is_dir($path)) {
312
				$permissions = \OCP\Constants::PERMISSION_ALL;
313
			} else {
314
				$permissions = \OCP\Constants::PERMISSION_ALL & ~\OCP\Constants::PERMISSION_CREATE;
315
			}
316
		}
317
318
		return $permissions;
319
	}
320
}
321