Completed
Push — federated-circles ( 80cab6...74a72b )
by Maxence
02:32
created

FederatedService::requestLinkStatus()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 22
rs 8.6737
c 2
b 0
f 0
cc 5
eloc 11
nc 4
nop 1
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Circles\Service;
28
29
30
use Exception;
31
use OC\Http\Client\ClientService;
32
use OCA\Circles\Db\CirclesMapper;
33
use OCA\Circles\Db\MembersMapper;
34
use OCA\Circles\Exceptions\FederatedCircleLinkFormatException;
35
use OCA\Circles\Exceptions\FederatedCircleNotAllowedException;
36
use OCA\Circles\Exceptions\CircleTypeNotValid;
37
use OCA\Circles\Exceptions\FederatedRemoteCircleDoesNotExistException;
38
use OCA\Circles\Exceptions\FederatedRemoteDoesNotAllowException;
39
use OCA\Circles\Exceptions\MemberIsNotAdminException;
40
use OCA\Circles\Model\Circle;
41
use OCA\Circles\Model\FederatedLink;
42
use OCP\IL10N;
43
use Sabre\HTTP\ClientException;
44
45
class FederatedService {
46
47
48
	const STATUS_ERROR = 0;
49
	const STATUS_LINK_DOWN = 1;
50
	const STATUS_REQUEST_DECLINED = 3;
51
	const STATUS_REQUEST_SENT = 6;
52
	const STATUS_LINK_UP = 9;
53
54
55
	/** @var string */
56
	private $userId;
57
58
	/** @var IL10N */
59
	private $l10n;
60
61
	/** @var ConfigService */
62
	private $configService;
63
64
	/** @var CirclesService */
65
	private $circlesService;
66
67
	/** @var CirclesMapper */
68
	private $dbCircles;
69
70
	/** @var MembersMapper */
71
	private $dbMembers;
72
73
	/** @var ClientService */
74
	private $clientService;
75
76
	/** @var MiscService */
77
	private $miscService;
78
79
80
	/**
81
	 * CirclesService constructor.
82
	 *
83
	 * @param $userId
84
	 * @param IL10N $l10n
85
	 * @param ConfigService $configService
86
	 * @param DatabaseService $databaseService
87
	 * @param CirclesService $circlesService
88
	 * @param ClientService $clientService
89
	 * @param MiscService $miscService
90
	 */
91 View Code Duplication
	public function __construct(
92
		$userId,
93
		IL10N $l10n,
94
		ConfigService $configService,
95
		DatabaseService $databaseService,
96
		CirclesService $circlesService,
97
		ClientService $clientService,
98
		MiscService $miscService
99
	) {
100
		$this->userId = $userId;
101
		$this->l10n = $l10n;
102
		$this->configService = $configService;
103
		$this->circlesService = $circlesService;
104
		$this->clientService = $clientService;
105
		$this->miscService = $miscService;
106
107
		$this->dbCircles = $databaseService->getCirclesMapper();
108
		$this->dbMembers = $databaseService->getMembersMapper();
109
	}
110
111
112
	/**
113
	 * link to a circle.
114
	 *
115
	 * @param int $circleId
116
	 * @param string $link
117
	 *
118
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
119
	 * @throws Exception
120
	 * @throws FederatedCircleLinkFormatException
121
	 * @throws CircleTypeNotValid
122
	 * @throws MemberIsNotAdminException
123
	 */
124
	public function linkCircle($circleId, $link) {
125
126
		if (!$this->configService->isFederatedAllowed()) {
127
			throw new FederatedCircleNotAllowedException(
128
				$this->l10n->t("Federated circles are not allowed on this Nextcloud")
129
			);
130
		}
131
132
		if (strpos($link, '@') === false) {
133
			throw new FederatedCircleLinkFormatException(
134
				$this->l10n->t("Federated link does not have a valid format")
135
			);
136
		}
137
138
		list($remoteCircle, $remoteAddress) = explode('@', $link, 2);
139
		try {
140
141
			$circle = $this->circlesService->detailsCircle($circleId);
142
			$circle->getUser()
143
				   ->hasToBeAdmin();
144
			$circle->cantBePersonal();
145
146
			return $this->requestLinkWithCircle($circle, $remoteAddress, $remoteCircle);
147
		} catch (Exception $e) {
148
			throw $e;
149
		}
150
	}
151
152
	/**
153
	 * @param string $remote
154
	 *
155
	 * @return string
156
	 */
157
	private function generateLinkRemoteURL($remote) {
158
		if (strpos($remote, 'http') !== 0) {
159
			$remote = 'https://' . $remote;
160
		}
161
162
		return rtrim($remote, '/') . '/index.php/apps/circles/circles/link/';
163
	}
164
165
166
	/**
167
	 * @param Circle $circle
168
	 * @param $remoteAddress
169
	 * @param $remoteCircle
170
	 *
171
	 * @return integer
172
	 * @throws Exception
173
	 */
174
	private function requestLinkWithCircle(Circle $circle, $remoteAddress, $remoteCircle) {
175
		$this->miscService->log(
176
			"create link : " . $remoteCircle . ' - ' . $remoteAddress . ' - ' . $circle->getId()
177
		);
178
179
		$args = [
180
			'sourceId'     => $circle->getId(),
181
			'sourceName'   => $circle->getName(),
182
			'circleName' => $remoteCircle
183
		];
184
185
		$client = $this->clientService->newClient();
186
		try {
187
			$request = $client->put(
188
				$this->generateLinkRemoteURL($remoteAddress), [
189
																'body'            => $args,
190
																'timeout'         => 10,
191
																'connect_timeout' => 10,
192
															]
193
			);
194
195
			$result = json_decode($request->getBody(), true);
196
			$this->requestLinkStatus($result);
197
198
			$this->miscService->log("_____RESULT: " . var_export($result, true));
199
200
			return $result['status'];
201
		} catch (Exception $e) {
202
			throw $e;
203
		}
204
	}
205
206
207
	private function requestLinkStatus($result) {
208
209
		if ($result['status'] === self::STATUS_REQUEST_SENT
210
			|| $result['status'] === self::STATUS_LINK_UP
211
		) {
212
			return;
213
		}
214
215
		if ($result['reason'] === 'federated_not_allowed') {
216
			throw new FederatedRemoteDoesNotAllowException(
217
				$this->l10n->t('Federated circles are not allowed on the remote Nextcloud')
218
			);
219
		}
220
221
		if ($result['reason'] === 'circle_does_not_exist') {
222
			throw new FederatedRemoteCircleDoesNotExistException(
223
				$this->l10n->t('The requested remote circle does not exist')
224
			);
225
		}
226
227
		throw new Exception();
228
	}
229
230
231
	/**
232
	 * @param Circle $circle
233
	 * @param $source
234
	 * @param FederatedLink $link
235
	 *
236
	 * @return bool
237
	 */
238
	public function initiateLink(Circle $circle, FederatedLink &$link) {
239
240
		$token = '';
241
		for ($i = 0; $i <= 5; $i++) {
242
			$token .= uniqid('', true);
243
		}
244
245
		$link->setToken($token);
246
247
		return ($circle->getType() === Circle::CIRCLES_PUBLIC);
248
	}
249
250
}