Completed
Push — master ( a2a170...aeff54 )
by Maxence
02:40
created

FederatedService   F

Complexity

Total Complexity 74

Size/Duplication

Total Lines 771
Duplicated Lines 4.67 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 12
Bugs 3 Features 0
Metric Value
wmc 74
c 12
b 3
f 0
lcom 1
cbo 20
dl 36
loc 771
rs 1.7254

23 Methods

Rating   Name   Duplication   Size   Complexity  
A linkCircle() 0 20 4
A updateFrameWithCloudId() 0 4 1
B __construct() 0 24 1
B linkStatus() 0 32 4
A eventOnLinkStatus() 0 16 4
B requestLinkWithCircle() 0 32 3
A generateLinkRemoteURL() 9 9 4
A generatePayloadDeliveryURL() 9 9 4
B requestLink() 0 43 4
A eventOnRequestLink() 0 16 4
B parseRequestLinkError() 0 34 6
A updateLinkFromRemote() 0 19 3
A checkUpdateLinkFromRemote() 0 10 3
A checkUpdateLinkFromRemoteLinkUp() 0 15 3
B checkUpdateLinkFromRemoteLinkRemove() 18 32 5
B updateLinkRemote() 0 24 2
A initiateLink() 0 19 3
A checkLinkRequestValidity() 0 13 4
B receiveFrame() 0 27 4
A getLink() 0 3 1
A getLinks() 0 3 1
B initiateRemoteShare() 0 27 2
B sendRemoteShare() 0 33 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FederatedService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FederatedService, and based on these observations, apply Extract Interface, too.

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\Api\v1\Circles;
33
use OCA\Circles\Db\CirclesRequest;
34
use OCA\Circles\Db\FederatedLinksRequest;
35
use OCA\Circles\Exceptions\CircleDoesNotExistException;
36
use OCA\Circles\Exceptions\FederatedCircleLinkFormatException;
37
use OCA\Circles\Exceptions\FederatedCircleNotAllowedException;
38
use OCA\Circles\Exceptions\CircleTypeNotValid;
39
use OCA\Circles\Exceptions\FederatedCircleStatusUpdateException;
40
use OCA\Circles\Exceptions\FederatedRemoteCircleDoesNotExistException;
41
use OCA\Circles\Exceptions\FederatedRemoteDoesNotAllowException;
42
use OCA\Circles\Exceptions\FederatedRemoteIsDown;
43
use OCA\Circles\Exceptions\SharingFrameAlreadyExistException;
44
use OCA\Circles\Exceptions\LinkCreationException;
45
use OCA\Circles\Exceptions\MemberIsNotAdminException;
46
use OCA\Circles\Model\Circle;
47
use OCA\Circles\Model\FederatedLink;
48
use OCA\Circles\Model\SharingFrame;
49
use OCP\IL10N;
50
51
class FederatedService {
52
53
	const REMOTE_URL_LINK = '/index.php/apps/circles/v1/link';
54
	const REMOTE_URL_PAYLOAD = '/index.php/apps/circles/v1/payload';
55
56
	/** @var string */
57
	private $userId;
58
59
	/** @var IL10N */
60
	private $l10n;
61
62
	/** @var CirclesRequest */
63
	private $circlesRequest;
64
65
	/** @var ConfigService */
66
	private $configService;
67
68
	/** @var CirclesService */
69
	private $circlesService;
70
71
	/** @var BroadcastService */
72
	private $broadcastService;
73
74
	/** @var FederatedLinksRequest */
75
	private $federatedLinksRequest;
76
77
	/** @var EventsService */
78
	private $eventsService;
79
80
	/** @var string */
81
	private $serverHost;
82
83
	/** @var ClientService */
84
	private $clientService;
85
86
	/** @var MiscService */
87
	private $miscService;
88
89
90
	/**
91
	 * CirclesService constructor.
92
	 *
93
	 * @param $userId
94
	 * @param IL10N $l10n
95
	 * @param CirclesRequest $circlesRequest
96
	 * @param ConfigService $configService
97
	 * @param CirclesService $circlesService
98
	 * @param BroadcastService $broadcastService
99
	 * @param FederatedLinksRequest $federatedLinksRequest
100
	 * @param EventsService $eventsService
101
	 * @param string $serverHost
102
	 * @param ClientService $clientService
103
	 * @param MiscService $miscService
104
	 */
105
	public function __construct(
106
		$userId,
107
		IL10N $l10n,
108
		CirclesRequest $circlesRequest,
109
		ConfigService $configService,
110
		CirclesService $circlesService,
111
		BroadcastService $broadcastService,
112
		FederatedLinksRequest $federatedLinksRequest,
113
		EventsService $eventsService, $serverHost,
114
		ClientService $clientService,
115
		MiscService $miscService
116
	) {
117
		$this->userId = $userId;
118
		$this->l10n = $l10n;
119
		$this->circlesRequest = $circlesRequest;
120
		$this->configService = $configService;
121
		$this->circlesService = $circlesService;
122
		$this->broadcastService = $broadcastService;
123
		$this->federatedLinksRequest = $federatedLinksRequest;
124
		$this->eventsService = $eventsService;
125
		$this->serverHost = (string)$serverHost;
126
		$this->clientService = $clientService;
127
		$this->miscService = $miscService;
128
	}
129
130
131
	/**
132
	 * linkCircle();
133
	 *
134
	 * link to a circle.
135
	 * Function will check if settings allow Federated links between circles, and the format of
136
	 * the link ($remote). If no exception, a request to the remote circle will be initiated
137
	 * using requestLinkWithCircle()
138
	 *
139
	 * $remote format: <circle_name>@<remote_host>
140
	 *
141
	 * @param int $circleId
142
	 * @param string $remote
143
	 *
144
	 * @throws Exception
145
	 * @throws FederatedCircleLinkFormatException
146
	 * @throws CircleTypeNotValid
147
	 *
148
	 * @return FederatedLink
149
	 */
150
	public function linkCircle($circleId, $remote) {
151
152
		if (!$this->configService->isFederatedCirclesAllowed()) {
153
			throw new FederatedCircleNotAllowedException(
154
				$this->l10n->t("Federated circles are not allowed on this Nextcloud")
155
			);
156
		}
157
158
		if (strpos($remote, '@') === false) {
159
			throw new FederatedCircleLinkFormatException(
160
				$this->l10n->t("Federated link does not have a valid format")
161
			);
162
		}
163
164
		try {
165
			return $this->requestLinkWithCircle($circleId, $remote);
166
		} catch (Exception $e) {
167
			throw $e;
168
		}
169
	}
170
171
172
	/**
173
	 * linkStatus()
174
	 *
175
	 * Update the status of a link.
176
	 * Function will check if user can edit the status, will update it and send the update to
177
	 * remote
178
	 *
179
	 * @param int $linkId
180
	 * @param int $status
181
	 *
182
	 * @throws Exception
183
	 * @throws FederatedCircleLinkFormatException
184
	 * @throws CircleTypeNotValid
185
	 * @throws MemberIsNotAdminException
186
	 *
187
	 * @return FederatedLink[]
188
	 */
189
	public function linkStatus($linkId, $status) {
190
191
		$status = (int)$status;
192
		$link = null;
193
		try {
194
195
			$link = $this->circlesRequest->getLinkFromId($linkId);
196
			$circle = $this->circlesRequest->getCircle($link->getCircleId(), $this->userId);
197
			$circle->hasToBeFederated();
198
			$circle->getHigherViewer()
199
				   ->hasToBeAdmin();
200
			$link->hasToBeValidStatusUpdate($status);
201
202
			if (!$this->eventOnLinkStatus($circle, $link, $status)) {
203
				return $this->circlesRequest->getLinksFromCircle($circle->getId());
204
			}
205
206
			$link->setStatus($status);
207
			$this->federatedLinksRequest->update($link);
208
209
		} catch (Exception $e) {
210
			throw $e;
211
		}
212
213
		try {
214
			$link->setUniqueId($circle->getUniqueId(true));
215
			$this->updateLinkRemote($link);
216
		} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
217
		}
218
219
		return $this->circlesRequest->getLinksFromCircle($circle->getId());
220
	}
221
222
223
	/**
224
	 * eventOnLinkStatus();
225
	 *
226
	 * Called by linkStatus() to manage events when status is changing.
227
	 * If status does not need update, returns false;
228
	 *
229
	 * @param Circle $circle
230
	 * @param FederatedLink $link
231
	 * @param $status
232
	 *
233
	 * @return bool
234
	 */
235
	private function eventOnLinkStatus(Circle $circle, FederatedLink $link, $status) {
236
		if ($link->getStatus() === $status) {
237
			return false;
238
		}
239
240
		if ($status === FederatedLink::STATUS_LINK_REMOVE) {
241
			$this->eventsService->onLinkRemove($circle, $link);
242
		}
243
244
		if ($status === FederatedLink::STATUS_LINK_UP) {
245
			$this->eventsService->onLinkRequestAccepting($circle, $link);
246
			$this->eventsService->onLinkUp($circle, $link);
247
		}
248
249
		return true;
250
	}
251
252
253
	/**
254
	 * requestLinkWithCircle()
255
	 *
256
	 * Using CircleId, function will get more infos from the database.
257
	 * Will check if author is at least admin and initiate a FederatedLink, save it
258
	 * in the database and send a request to the remote circle using requestLink()
259
	 * If any issue, entry is removed from the database.
260
	 *
261
	 * @param int $circleId
262
	 * @param string $remote
263
	 *
264
	 * @return FederatedLink
265
	 * @throws Exception
266
	 */
267
	private function requestLinkWithCircle($circleId, $remote) {
268
269
		$link = null;
270
		try {
271
			list($remoteCircle, $remoteAddress) = explode('@', $remote, 2);
272
273
			$circle = $this->circlesService->detailsCircle($circleId);
274
			$circle->getHigherViewer()
275
				   ->hasToBeAdmin();
276
			$circle->hasToBeFederated();
277
			$circle->cantBePersonal();
278
279
			$link = new FederatedLink();
280
			$link->setCircleId($circleId)
281
				 ->setLocalAddress($this->serverHost)
282
				 ->setAddress($remoteAddress)
283
				 ->setRemoteCircleName($remoteCircle)
284
				 ->setStatus(FederatedLink::STATUS_LINK_SETUP)
285
				 ->generateToken();
286
287
			$this->federatedLinksRequest->create($link);
288
			$this->requestLink($circle, $link);
289
290
		} catch (Exception $e) {
291
			if ($link !== null) {
292
				$this->federatedLinksRequest->delete($link);
293
			}
294
			throw $e;
295
		}
296
297
		return $link;
298
	}
299
300
301
	/**
302
	 * @param string $remote
303
	 *
304
	 * @return string
305
	 */
306 View Code Duplication
	private function generateLinkRemoteURL($remote) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
		if ((!$this->configService->isNonSSLLinksAllowed() || strpos($remote, 'http://') !== 0)
308
			&& strpos($remote, 'https://') !== 0
309
		) {
310
			$remote = 'https://' . $remote;
311
		}
312
313
		return rtrim($remote, '/') . self::REMOTE_URL_LINK;
314
	}
315
316
317
	/**
318
	 * @param string $remote
319
	 *
320
	 * @return string
321
	 */
322 View Code Duplication
	private function generatePayloadDeliveryURL($remote) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
323
		if ((!$this->configService->isNonSSLLinksAllowed() || strpos($remote, 'http://') !== 0)
324
			&& strpos($remote, 'https://') !== 0
325
		) {
326
			$remote = 'https://' . $remote;
327
		}
328
329
		return rtrim($remote, '/') . self::REMOTE_URL_PAYLOAD;
330
	}
331
332
333
	/**
334
	 * requestLink()
335
	 *
336
	 *
337
	 * @param Circle $circle
338
	 * @param FederatedLink $link
339
	 *
340
	 * @return boolean
341
	 * @throws Exception
342
	 */
343
	private function requestLink(Circle $circle, FederatedLink & $link) {
344
		$args = [
345
			'apiVersion' => Circles::API_VERSION,
346
			'token'      => $link->getToken(true),
347
			'uniqueId'   => $circle->getUniqueId(true),
348
			'sourceName' => $circle->getName(),
349
			'linkTo'     => $link->getRemoteCircleName(),
350
			'address'    => $link->getLocalAddress()
351
		];
352
353
		$client = $this->clientService->newClient();
354
355
		try {
356
			$request = $client->put(
357
				$this->generateLinkRemoteURL($link->getAddress()), [
358
																	 'body'            => $args,
359
																	 'timeout'         => 10,
360
																	 'connect_timeout' => 10,
361
																 ]
362
			);
363
364
			$result = json_decode($request->getBody(), true);
365
			if ($result === null) {
366
				throw new FederatedRemoteIsDown(
367
					$this->l10n->t(
368
						'The remote host is down or the Circles app is not installed on it'
369
					)
370
				);
371
			}
372
373
			$this->eventOnRequestLink(
374
				$circle, $link, $result['status'],
375
				((key_exists('reason', $result)) ? $result['reason'] : '')
376
			);
377
378
			$link->setUniqueId($result['uniqueId']);
379
			$this->federatedLinksRequest->update($link);
380
381
			return true;
382
		} catch (Exception $e) {
383
			throw $e;
384
		}
385
	}
386
387
388
	/**
389
	 * eventOnRequestLink();
390
	 *
391
	 * Called by requestLink() will update status and event
392
	 * Will also manage errors returned by the remote link
393
	 *
394
	 * @param Circle $circle
395
	 * @param FederatedLink $link
396
	 * @param $status
397
	 * @param $reason
398
	 *
399
	 * @throws Exception
400
	 */
401
	private function eventOnRequestLink(Circle $circle, FederatedLink $link, $status, $reason) {
402
403
		try {
404
			if ($status === FederatedLink::STATUS_LINK_UP) {
405
				$link->setStatus(FederatedLink::STATUS_LINK_UP);
406
				$this->eventsService->onLinkUp($circle, $link);
407
			} else if ($status === FederatedLink::STATUS_LINK_REQUESTED) {
408
				$link->setStatus(FederatedLink::STATUS_REQUEST_SENT);
409
				$this->eventsService->onLinkRequestSent($circle, $link);
410
			} else {
411
				$this->parseRequestLinkError($reason);
412
			}
413
		} catch (Exception $e) {
414
			throw $e;
415
		}
416
	}
417
418
419
	/**
420
	 * parseRequestLinkError();
421
	 *
422
	 * Will parse the error reason returned by requestLink() and throw an Exception
423
	 *
424
	 * @param $reason
425
	 *
426
	 * @throws Exception
427
	 * @throws FederatedRemoteCircleDoesNotExistException
428
	 * @throws FederatedRemoteDoesNotAllowException
429
	 */
430
	private function parseRequestLinkError($reason) {
431
432
		if ($reason === 'federated_not_allowed') {
433
			throw new FederatedRemoteDoesNotAllowException(
434
				$this->l10n->t('Federated circles are not allowed on the remote Nextcloud')
435
			);
436
		}
437
438
		if ($reason === 'circle_links_disable') {
439
			throw new FederatedRemoteDoesNotAllowException(
440
				$this->l10n->t('The remote circle does not accept Federated Links')
441
			);
442
		}
443
444
		if ($reason === 'duplicate_unique_id') {
445
			throw new FederatedRemoteDoesNotAllowException(
446
				$this->l10n->t('It seems that you are trying to link a circle to itself')
447
			);
448
		}
449
450
		if ($reason === 'duplicate_link') {
451
			throw new FederatedRemoteDoesNotAllowException(
452
				$this->l10n->t('This link exists already')
453
			);
454
		}
455
456
		if ($reason === 'circle_does_not_exist') {
457
			throw new FederatedRemoteCircleDoesNotExistException(
458
				$this->l10n->t('The requested remote circle does not exist')
459
			);
460
		}
461
462
		throw new Exception($reason);
463
	}
464
465
466
	/**
467
	 * @param $token
468
	 * @param $uniqueId
469
	 * @param $status
470
	 *
471
	 * @return FederatedLink
472
	 * @throws Exception
473
	 */
474
	public function updateLinkFromRemote($token, $uniqueId, $status) {
475
		try {
476
			$link = $this->circlesRequest->getLinkFromToken($token, $uniqueId);
477
			$circle = $this->circlesRequest->forceGetCircle($link->getCircleId());
478
			$circle->hasToBeFederated();
479
480
			$this->checkUpdateLinkFromRemote($status);
481
			$this->checkUpdateLinkFromRemoteLinkUp($circle, $link, $status);
482
			$this->checkUpdateLinkFromRemoteLinkRemove($circle, $link, $status);
483
484
			if ($link->getStatus() !== $status) {
485
				$this->federatedLinksRequest->update($link);
486
			}
487
488
			return $link;
489
		} catch (Exception $e) {
490
			throw $e;
491
		}
492
	}
493
494
	/**
495
	 * checkUpdateLinkFromRemote();
496
	 *
497
	 * will throw exception is the status sent by remote is not correct
498
	 *
499
	 * @param $status
500
	 *
501
	 * @throws FederatedCircleStatusUpdateException
502
	 */
503
	private function checkUpdateLinkFromRemote($status) {
504
		$status = (int)$status;
505
		if ($status !== FederatedLink::STATUS_LINK_UP
506
			&& $status !== FederatedLink::STATUS_LINK_REMOVE
507
		) {
508
			throw new FederatedCircleStatusUpdateException(
509
				$this->l10n->t('Cannot proceed with this status update')
510
			);
511
		}
512
	}
513
514
515
	/**
516
	 * checkUpdateLinkFromRemoteLinkUp()
517
	 *
518
	 * in case of a request of status update from remote for a link up, we check the current
519
	 * status of the link locally.
520
	 *
521
	 * @param Circle $circle
522
	 * @param FederatedLink $link
523
	 * @param $status
524
	 *
525
	 * @throws FederatedCircleStatusUpdateException
526
	 */
527
	private function checkUpdateLinkFromRemoteLinkUp(Circle $circle, FederatedLink $link, $status) {
528
		if ((int)$status !== FederatedLink::STATUS_LINK_UP) {
529
			return;
530
		}
531
532
		if ($link->getStatus() !== FederatedLink::STATUS_REQUEST_SENT) {
533
			throw new FederatedCircleStatusUpdateException(
534
				$this->l10n->t('Cannot proceed with this status update')
535
			);
536
		}
537
538
		$this->eventsService->onLinkRequestAccepted($circle, $link);
539
		$this->eventsService->onLinkUp($circle, $link);
540
		$link->setStatus($status);
541
	}
542
543
544
	/**
545
	 * checkUpdateLinkFromRemoteLinkRemove();
546
	 *
547
	 * in case of a request of status update from remote for a link down, we check the current
548
	 * status of the link locally
549
	 *
550
	 * @param Circle $circle
551
	 * @param FederatedLink $link
552
	 * @param $status
553
	 *
554
	 * @throws FederatedCircleStatusUpdateException
555
	 */
556
	private function checkUpdateLinkFromRemoteLinkRemove(
557
		Circle $circle, FederatedLink $link, $status
558
	) {
559
		if ((int)$status !== FederatedLink::STATUS_LINK_REMOVE) {
560
			return;
561
		}
562
563 View Code Duplication
		if ($link->getStatus() === FederatedLink::STATUS_REQUEST_SENT) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
564
			$link->setStatus(FederatedLink::STATUS_REQUEST_DECLINED);
565
			$this->eventsService->onLinkRequestRejected($circle, $link);
566
567
			return;
568
		}
569
570 View Code Duplication
		if ($link->getStatus() === FederatedLink::STATUS_LINK_REQUESTED) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
571
			$link->setStatus(FederatedLink::STATUS_LINK_REMOVE);
572
			$this->eventsService->onLinkRequestCanceled($circle, $link);
573
574
			return;
575
		}
576
577 View Code Duplication
		if ($link->getStatus() > FederatedLink::STATUS_LINK_DOWN) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
578
			$link->setStatus(FederatedLink::STATUS_LINK_DOWN);
579
			$this->eventsService->onLinkDown($circle, $link);
580
581
			return;
582
		}
583
584
		throw new FederatedCircleStatusUpdateException(
585
			$this->l10n->t('Cannot proceed with this status update')
586
		);
587
	}
588
589
590
	/**
591
	 * updateLinkRemote()
592
	 *
593
	 * Send a request to the remote of the link to update its status.
594
	 *
595
	 * @param FederatedLink $link
596
	 *
597
	 * @return boolean
598
	 * @throws Exception
599
	 */
600
	public function updateLinkRemote(FederatedLink & $link) {
601
		$args = [
602
			'apiVersion' => Circles::API_VERSION,
603
			'token'      => $link->getToken(true),
604
			'uniqueId'   => $link->getUniqueId(true),
605
			'status'     => $link->getStatus()
606
		];
607
608
		$client = $this->clientService->newClient();
609
610
		try {
611
			$client->post(
612
				$this->generateLinkRemoteURL($link->getAddress()), [
613
																	 'body'            => $args,
614
																	 'timeout'         => 10,
615
																	 'connect_timeout' => 10,
616
																 ]
617
			);
618
619
			return true;
620
		} catch (Exception $e) {
621
			throw $e;
622
		}
623
	}
624
625
626
	/**
627
	 * Create a new link into database and assign the correct status.
628
	 *
629
	 * @param Circle $circle
630
	 * @param FederatedLink $link
631
	 *
632
	 * @throws Exception
633
	 */
634
	public function initiateLink(Circle $circle, FederatedLink & $link) {
635
636
		try {
637
			$this->checkLinkRequestValidity($circle, $link);
638
			$link->setCircleId($circle->getId());
639
640
			if ($circle->getSetting('allow_links_auto') === 'true') {
641
				$link->setStatus(FederatedLink::STATUS_LINK_UP);
642
				$this->eventsService->onLinkUp($circle, $link);
643
			} else {
644
				$link->setStatus(FederatedLink::STATUS_LINK_REQUESTED);
645
				$this->eventsService->onLinkRequestReceived($circle, $link);
646
			}
647
648
			$this->federatedLinksRequest->create($link);
649
		} catch (Exception $e) {
650
			throw $e;
651
		}
652
	}
653
654
655
	/**
656
	 * @param Circle $circle
657
	 * @param FederatedLink $link
658
	 *
659
	 * @throws LinkCreationException
660
	 */
661
	private function checkLinkRequestValidity($circle, $link) {
662
		if ($circle->getUniqueId(true) === $link->getUniqueId(true)) {
663
			throw new LinkCreationException('duplicate_unique_id');
664
		}
665
666
		if ($this->getLink($circle->getId(), $link->getUniqueId(true)) !== null) {
667
			throw new LinkCreationException('duplicate_link');
668
		}
669
670
		if ($circle->getSetting('allow_links') !== 'true') {
671
			throw new LinkCreationException('circle_links_disable');
672
		}
673
	}
674
675
676
	/**
677
	 * @param string $token
678
	 * @param string $uniqueId
679
	 * @param SharingFrame $frame
680
	 *
681
	 * @return bool
682
	 * @throws Exception
683
	 */
684
	public function receiveFrame($token, $uniqueId, SharingFrame & $frame) {
685
		try {
686
687
			$link = $this->circlesRequest->getLinkFromToken((string)$token, (string)$uniqueId);
688
		} catch (Exception $e) {
689
			throw $e;
690
		}
691
692
		if ($this->circlesRequest->getFrame($link->getCircleId(), $frame->getUniqueId())) {
693
			//		$this->miscService->log("Frame already exist");
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
694
			throw new SharingFrameAlreadyExistException('shares_is_already_known');
695
		}
696
697
		try {
698
			$circle = $this->circlesRequest->forceGetCircle($link->getCircleId());
699
		} catch (CircleDoesNotExistException $e) {
700
			throw new CircleDoesNotExistException('unknown_circle');
701
		}
702
703
		$frame->setCircleId($link->getCircleId());
704
		$frame->setCircleName($circle->getName());
705
706
		$this->circlesRequest->saveFrame($frame);
707
		$this->broadcastService->broadcastFrame($frame->getHeader('broadcast'), $frame);
708
709
		return true;
710
	}
711
712
	/**
713
	 * @param int $circleId
714
	 * @param string $uniqueId
715
	 *
716
	 * @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...
717
	 */
718
	public function getLink($circleId, $uniqueId) {
719
		return $this->federatedLinksRequest->getFromUniqueId($circleId, $uniqueId);
720
	}
721
722
723
	/**
724
	 * @param int $circleId
725
	 *
726
	 * @return FederatedLink[]
727
	 */
728
	public function getLinks($circleId) {
729
		return $this->federatedLinksRequest->getLinked($circleId);
730
	}
731
732
733
	/**
734
	 * @param int $circleId
735
	 * @param string $uniqueId
736
	 *
737
	 * @return bool
738
	 * @throws Exception
739
	 */
740
	public function initiateRemoteShare($circleId, $uniqueId) {
741
		$args = [
742
			'apiVersion' => Circles::API_VERSION,
743
			'circleId'   => (int)$circleId,
744
			'uniqueId'   => (string)$uniqueId
745
		];
746
747
		$client = $this->clientService->newClient();
748
		try {
749
			$request = $client->post(
750
				$this->generatePayloadDeliveryURL($this->serverHost), [
751
																		'body'            => $args,
752
																		'timeout'         => 10,
753
																		'connect_timeout' => 10,
754
																	]
755
			);
756
757
			$result = json_decode($request->getBody(), true);
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
758
//			$this->miscService->log(
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
759
//				"initiateRemoteShare result: " . $uniqueId . '  ----  ' . var_export($result, true)
760
//			);
761
762
			return true;
763
		} catch (Exception $e) {
764
			throw $e;
765
		}
766
	}
767
768
769
	/**
770
	 * @param SharingFrame $frame
771
	 *
772
	 * @throws Exception
773
	 */
774
	public function sendRemoteShare(SharingFrame $frame) {
775
776
		$circle = $this->circlesRequest->forceGetCircle($frame->getCircleId());
777
		if ($circle === null) {
778
			throw new Exception('unknown_circle');
779
		}
780
781
		$links = $this->getLinks($frame->getCircleId());
782
		foreach ($links AS $link) {
783
784
			$args = [
785
				'apiVersion' => Circles::API_VERSION,
786
				'token'      => $link->getToken(true),
787
				'uniqueId'   => $circle->getUniqueId(true),
788
				'item'       => json_encode($frame)
789
			];
790
791
			$client = $this->clientService->newClient();
792
			try {
793
				$client->put(
794
					$this->generatePayloadDeliveryURL($link->getAddress()), [
795
																			  'body'            => $args,
796
																			  'timeout'         => 10,
797
																			  'connect_timeout' => 10,
798
																		  ]
799
				);
800
			} catch (Exception $e) {
801
				$this->miscService->log(
802
					'Could not connect to ' . $link->getAddress() . ' - ' . $e->getMessage()
803
				);
804
			}
805
		}
806
	}
807
808
809
	/**
810
	 * generateHeaders()
811
	 *
812
	 * Generate new headers for the current Payload, and save them in the SharingFrame.
813
	 *
814
	 * @param SharingFrame $frame
815
	 */
816
	public function updateFrameWithCloudId(SharingFrame $frame) {
817
		$frame->setCloudId($this->serverHost);
818
		$this->circlesRequest->updateFrame($frame);
819
	}
820
821
}