Completed
Pull Request — master (#32303)
by Victor
10:56
created

DiscoveryManager::ocmDiscover()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 6
nop 1
dl 0
loc 36
rs 8.0995
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Lukas Reschke <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 * @author Vincent Petry <[email protected]>
7
 *
8
 * @copyright Copyright (c) 2018, ownCloud GmbH
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OCA\FederatedFileSharing;
26
27
use GuzzleHttp\Exception\ClientException;
28
use GuzzleHttp\Exception\ConnectException;
29
use OCP\Http\Client\IClient;
30
use OCP\Http\Client\IClientService;
31
use OCP\ICache;
32
use OCP\ICacheFactory;
33
34
/**
35
 * Class DiscoveryManager handles the discovery of endpoints used by Federated
36
 * Cloud Sharing.
37
 *
38
 * @package OCA\FederatedFileSharing
39
 */
40
class DiscoveryManager {
41
	/** @var ICache */
42
	private $cache;
43
	/** @var IClient */
44
	private $client;
45
	/** @var bool */
46
	public $underTest = false;
47
48
	/**
49
	 * @param ICacheFactory $cacheFactory
50
	 * @param IClientService $clientService
51
	 */
52
	public function __construct(ICacheFactory $cacheFactory,
53
								IClientService $clientService) {
54
		$this->cache = $cacheFactory->create('ocs-discovery');
55
		$this->client = $clientService->newClient();
56
	}
57
58
	/**
59
	 * Returns whether the specified URL includes only safe characters, if not
60
	 * returns false
61
	 *
62
	 * @param string $url
63
	 * @return bool
64
	 */
65
	private function isSafeUrl($url) {
66
		return (bool)\preg_match('/^[\/\.A-Za-z0-9]+$/', $url);
67
	}
68
69
	/**
70
	 * Discover the actual data and do some naive caching to ensure that the data
71
	 * is not requested multiple times.
72
	 *
73
	 * If no valid discovery data is found the ownCloud defaults are returned.
74
	 *
75
	 * @param string $remote
76
	 * @return array
77
	 */
78
	private function discover($remote) {
79
		// Check if something is in the cache
80
		if ($cacheData = $this->cache->get($remote)) {
81
			return \json_decode($cacheData, true);
82
		}
83
84
		// Default response body
85
		$discoveredServices = [
86
			'webdav' => '/public.php/webdav',
87
			'share' => '/ocs/v1.php/cloud/shares',
88
		];
89
90
		if (\defined('PHPUNIT_RUN') && !$this->underTest) {
91
			return $discoveredServices;
92
		}
93
94
		$decodedService = $this->makeRequest($remote . '/ocs-provider/');
95
		if (!empty($decodedService)) {
96
			$endpoints = [
97
				'webdav',
98
				'share',
99
			];
100
101
			foreach ($endpoints as $endpoint) {
102
				if (isset($decodedService['services']['FEDERATED_SHARING']['endpoints'][$endpoint])) {
103
					$endpointUrl = (string)$decodedService['services']['FEDERATED_SHARING']['endpoints'][$endpoint];
104
					if ($this->isSafeUrl($endpointUrl)) {
105
						$discoveredServices[$endpoint] = $endpointUrl;
106
					}
107
				}
108
			}
109
		}
110
111
		// Write into cache
112
		$this->cache->set($remote, \json_encode($discoveredServices));
113
		return $discoveredServices;
114
	}
115
116
	/**
117
	 * Discover the actual data and do some naive caching to ensure that the data
118
	 * is not requested multiple times.
119
	 *
120
	 * If no valid discovery data is found the ownCloud defaults are returned.
121
	 *
122
	 * @param string $remote
123
	 * @return array
124
	 */
125
	private function ocmDiscover($remote) {
126
		// Check if something is in the cache
127
		if ($cacheData = $this->cache->get('OCM' . $remote)) {
128
			return \json_decode($cacheData, true);
129
		}
130
131
		// Default response body
132
		$discoveredServices = [
133
			'webdav' => '/public.php/webdav',
134
			'ocm' => '/index.php/apps/federatedfilesharing',
135
		];
136
137
		if (\defined('PHPUNIT_RUN') && !$this->underTest) {
138
			return $discoveredServices;
139
		}
140
141
		$decodedService = $this->makeRequest($remote . '/ocm-provider/');
142
		if (!empty($decodedService) && $decodedService['enabled'] === true) {
143
			$discoveredServices['ocm'] = $decodedService['endPoint'];
144
			$shareTypes = $decodedService['shareTypes'];
145
			foreach ($shareTypes as $type) {
146
				if ($type['name'] == 'file') {
147
					$discoveredServices['webdav'] = $type['protocols']['webdav'];
148
				}
149
			}
150
		} else {
151
			return [
152
				'webdav' => false,
153
				'ocm' => false,
154
			];
155
		}
156
157
		// Write into cache
158
		$this->cache->set('OCM' . $remote, \json_encode($discoveredServices));
159
		return $discoveredServices;
160
	}
161
162
	/**
163
	 * Send GET request and return decoded body of response on success
164
	 *
165
	 * @param $url
166
	 *
167
	 * @return array
168
	 */
169
	private function makeRequest($url) {
170
		$decodedService = [];
171
		try {
172
			// make timeout configurable?
173
			$response = $this->client->get(
174
				$url,
175
				[
176
					'timeout' => 10,
177
					'connect_timeout' => 10,
178
				]
179
			);
180
			if ($response->getStatusCode() === 200) {
181
				$decodedService = \json_decode($response->getBody(), true);
182
			}
183
			if (\is_array($decodedService) === false) {
184
				$decodedService = [];
185
			}
186
		} catch (ClientException $e) {
187
			// Don't throw any exception since exceptions are handled before
188
		} catch (ConnectException $e) {
189
			// Don't throw any exception since exceptions are handled before
190
		}
191
		return $decodedService;
192
	}
193
194
	/**
195
	 * Return the public WebDAV endpoint used by the specified remote
196
	 *
197
	 * @param string $host
198
	 * @return string
199
	 */
200
	public function getWebDavEndpoint($host) {
201
		$ocmWebDavEndpoint = $this->ocmDiscover($host)['webdav'];
202
		return ($ocmWebDavEndpoint !== false)
203
			? $ocmWebDavEndpoint
204
			: $this->discover($host)['webdav'];
205
	}
206
207
	/**
208
	 * Return the sharing endpoint used by the specified remote
209
	 *
210
	 * @param string $host
211
	 * @return string
212
	 */
213
	public function getShareEndpoint($host) {
214
		return $this->discover($host)['share'];
215
	}
216
217
	public function getOcmShareEndPoint($host) {
218
		return $this->ocmDiscover($host)['ocm'];
219
	}
220
}
221