Completed
Push — federated-circles ( 47921f...25ee14 )
by Maxence
04:23
created

FederatedService   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 286
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 20
c 4
b 0
f 1
lcom 1
cbo 11
dl 0
loc 286
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 24 1
A linkCircle() 0 20 4
A generateLinkRemoteURL() 0 7 2
B requestLinkWithCircle() 0 29 2
A requestLink() 0 59 3
B parsingRequestLinkResult() 0 28 5
A initiateLink() 0 12 2
A getLink() 0 3 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\FederatedLinksRequest;
34
use OCA\Circles\Db\MembersMapper;
35
use OCA\Circles\Exceptions\FederatedCircleLinkFormatException;
36
use OCA\Circles\Exceptions\FederatedCircleNotAllowedException;
37
use OCA\Circles\Exceptions\CircleTypeNotValid;
38
use OCA\Circles\Exceptions\FederatedLinkCreationIssue;
39
use OCA\Circles\Exceptions\FederatedRemoteCircleDoesNotExistException;
40
use OCA\Circles\Exceptions\FederatedRemoteDoesNotAllowException;
41
use OCA\Circles\Exceptions\MemberIsNotAdminException;
42
use OCA\Circles\Model\Circle;
43
use OCA\Circles\Model\FederatedLink;
44
use OCP\IL10N;
45
46
class FederatedService {
47
48
49
	/** @var string */
50
	private $userId;
51
52
	/** @var IL10N */
53
	private $l10n;
54
55
	/** @var ConfigService */
56
	private $configService;
57
58
	/** @var CirclesService */
59
	private $circlesService;
60
61
	/** @var FederatedLinksRequest */
62
	private $federatedLinksRequest;
63
64
	/** @var CirclesMapper */
65
	private $dbCircles;
66
67
	/** @var MembersMapper */
68
	private $dbMembers;
69
70
	/** @var ClientService */
71
	private $clientService;
72
73
	/** @var MiscService */
74
	private $miscService;
75
76
77
	/**
78
	 * CirclesService constructor.
79
	 *
80
	 * @param $userId
81
	 * @param IL10N $l10n
82
	 * @param ConfigService $configService
83
	 * @param DatabaseService $databaseService
84
	 * @param CirclesService $circlesService
85
	 * @param FederatedLinksRequest $federatedLinksRequest
86
	 * @param string $serverHost
87
	 * @param ClientService $clientService
88
	 * @param MiscService $miscService
89
	 */
90
	public function __construct(
91
		$userId,
92
		IL10N $l10n,
93
		ConfigService $configService,
94
		DatabaseService $databaseService,
95
		CirclesService $circlesService,
96
		FederatedLinksRequest $federatedLinksRequest,
97
		string $serverHost,
98
		ClientService $clientService,
99
		MiscService $miscService
100
	) {
101
		$this->userId = $userId;
102
		$this->l10n = $l10n;
103
		$this->configService = $configService;
104
		$this->circlesService = $circlesService;
105
		$this->federatedLinksRequest = $federatedLinksRequest;
106
		$this->serverHost = $serverHost;
0 ignored issues
show
Bug introduced by
The property serverHost does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
107
		$this->clientService = $clientService;
108
109
		$this->miscService = $miscService;
110
111
		$this->dbCircles = $databaseService->getCirclesMapper();
112
		$this->dbMembers = $databaseService->getMembersMapper();
113
	}
114
115
116
	/**
117
	 * link to a circle.
118
	 *
119
	 * @param int $circleId
120
	 * @param string $remote
121
	 *
122
	 * @throws Exception
123
	 * @throws FederatedCircleLinkFormatException
124
	 * @throws CircleTypeNotValid
125
	 * @throws MemberIsNotAdminException
126
	 *
127
	 * @return FederatedLink
128
	 */
129
	public function linkCircle($circleId, $remote) {
130
131
		if (!$this->configService->isFederatedAllowed()) {
132
			throw new FederatedCircleNotAllowedException(
133
				$this->l10n->t("Federated circles are not allowed on this Nextcloud")
134
			);
135
		}
136
137
		if (strpos($remote, '@') === false) {
138
			throw new FederatedCircleLinkFormatException(
139
				$this->l10n->t("Federated link does not have a valid format")
140
			);
141
		}
142
143
		try {
144
			return $this->requestLinkWithCircle($circleId, $remote);
145
		} catch (Exception $e) {
146
			throw $e;
147
		}
148
	}
149
150
151
	/**
152
	 * @param $circleId
153
	 * @param $remote
154
	 *
155
	 * @return FederatedLink
156
	 * @throws Exception
157
	 */
158
	private function requestLinkWithCircle($circleId, $remote) {
159
160
		$link = null;
161
		try {
162
			list($remoteCircle, $remoteAddress) = explode('@', $remote, 2);
163
164
			$circle = $this->circlesService->detailsCircle($circleId);
165
			$circle->getUser()
166
				   ->hasToBeAdmin();
167
			$circle->cantBePersonal();
168
169
			$link = new FederatedLink();
170
			$link->setCircleId($circleId)
171
				 ->setLocalAddress($this->serverHost)
172
				 ->setAddress($remoteAddress)
173
				 ->setRemoteCircleName($remoteCircle)
174
				 ->setStatus(FederatedLink::STATUS_LINK_SETUP)
175
				 ->generateToken();
176
177
			$this->federatedLinksRequest->create($link);
178
			$this->requestLink($circle, $link);
179
180
		} catch (Exception $e) {
181
			$this->federatedLinksRequest->delete($link);
0 ignored issues
show
Bug introduced by
It seems like $link defined by null on line 160 can be null; however, OCA\Circles\Db\FederatedLinksRequest::delete() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
182
			throw $e;
183
		}
184
185
		return $link;
186
	}
187
188
189
	/**
190
	 * @param string $remote
191
	 *
192
	 * @return string
193
	 */
194
	private function generateLinkRemoteURL($remote) {
195
		if (strpos($remote, 'http') !== 0) {
196
			$remote = 'https://' . $remote;
197
		}
198
199
		return rtrim($remote, '/') . '/index.php/apps/circles/circles/link/';
200
	}
201
202
203
	/**
204
	 * @param Circle $circle
205
	 * @param FederatedLink $link
206
	 *
207
	 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

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...
208
	 * @throws Exception
209
	 */
210
	private function requestLink(Circle $circle, FederatedLink &$link) {
211
		$args = [
212
			'token'      => $link->getToken(),
213
			'uniqueId'   => $circle->getUniqueId(),
214
			'sourceName' => $circle->getName(),
215
			'linkTo'     => $link->getRemoteCircleName(),
216
			'address'    => $link->getLocalAddress()
217
		];
218
219
220
//		$this->miscService->log(microtime());
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
221
//		$ch = curl_init();
222
//
223
//		curl_setopt($ch, CURLOPT_URL, 'https://92i.kh.ro/test.php');
224
//		curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
225
//		curl_setopt($ch, CURLOPT_TIMEOUT_MS, 30000);
226
//		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 30000);
227
//
228
//		curl_exec($ch);
229
//		curl_close($ch);
230
//
231
//		$this->miscService->log(microtime());
232
233
		$client = $this->clientService->newClient();
234
235
		// TEST TEST TEST
236
		try {
237
			$client->put(
238
				'http://nextcloud/index.php/apps/circles/circles/test', [
239
				'body'            => $args,
240
				'timeout'         => 10,
241
				'connect_timeout' => 10,
242
			]
243
			);
244
245
246
			$request = $client->put(
247
				$this->generateLinkRemoteURL($link->getAddress()), [
248
																	 'body'            => $args,
249
																	 'timeout'         => 10,
250
																	 'connect_timeout' => 10,
251
																 ]
252
			);
253
254
			$result = json_decode($request->getBody(), true);
255
256
			$link->setStatus($result['status']);
257
			if (!$link->isValid()) {
258
				$this->parsingRequestLinkResult($result);
259
			}
260
261
			$link->setUniqueId($result['uniqueId']);
262
			$this->federatedLinksRequest->update($link);
263
264
			return true;
265
		} catch (Exception $e) {
266
			throw $e;
267
		}
268
	}
269
270
271
	private function parsingRequestLinkResult($result) {
272
273
		if ($result['reason'] === 'federated_not_allowed') {
274
			throw new FederatedRemoteDoesNotAllowException(
275
				$this->l10n->t('Federated circles are not allowed on the remote Nextcloud')
276
			);
277
		}
278
279
		if ($result['reason'] === 'duplicate_unique_id') {
280
			throw new FederatedRemoteDoesNotAllowException(
281
				$this->l10n->t('It seems that you are trying to link a circle to itself')
282
			);
283
		}
284
285
		if ($result['reason'] === 'duplicate_link') {
286
			throw new FederatedRemoteDoesNotAllowException(
287
				$this->l10n->t('This link exists already')
288
			);
289
		}
290
291
		if ($result['reason'] === 'circle_does_not_exist') {
292
			throw new FederatedRemoteCircleDoesNotExistException(
293
				$this->l10n->t('The requested remote circle does not exist')
294
			);
295
		}
296
297
		throw new Exception();
298
	}
299
300
301
	/**
302
	 * @param Circle $circle
303
	 * @param FederatedLink $link
304
	 *
305
	 * @return bool
306
	 */
307
	public function initiateLink(Circle $circle, FederatedLink &$link) {
308
309
		$link->setCircleId($circle->getId());
310
311
		if ($circle->getType() === Circle::CIRCLES_PUBLIC) {
312
			$link->setStatus(FederatedLink::STATUS_LINK_UP);
313
		} else {
314
			$link->setStatus(FederatedLink::STATUS_REQUEST_SENT);
315
		}
316
317
		return $this->federatedLinksRequest->create($link);
318
	}
319
320
321
	/**
322
	 * @param $circleId
323
	 * @param $uniqueId
324
	 *
325
	 * @return FederatedLink
0 ignored issues
show
Documentation introduced by
Should the return type not be FederatedLink|null?

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...
326
	 */
327
	public function getLink($circleId, $uniqueId) {
328
		return $this->federatedLinksRequest->get($circleId, $uniqueId);
329
	}
330
331
}