Completed
Push — master ( f9a32b...1fc0d2 )
by Maxence
04:23
created

SharingFrameService::createFrame()   A

Complexity

Conditions 2
Paths 7

Size

Total Lines 17
Code Lines 11

Duplication

Lines 17
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 17
loc 17
rs 9.4285
c 1
b 0
f 0
cc 2
eloc 11
nc 7
nop 3
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\AppInfo\Application;
34
use OCA\Circles\Db\CirclesRequest;
35
use OCA\Circles\Db\FederatedLinksRequest;
36
use OCA\Circles\Exceptions\CircleDoesNotExistException;
37
use OCA\Circles\Exceptions\MemberDoesNotExistException;
38
use OCA\Circles\Exceptions\PayloadDeliveryException;
39
use OCA\Circles\Exceptions\SharingFrameAlreadyDeliveredException;
40
use OCA\Circles\Exceptions\SharingFrameAlreadyExistException;
41
use OCA\Circles\Exceptions\SharingFrameDoesNotExistException;
42
use OCA\Circles\Model\Circle;
43
use OCA\Circles\Model\FederatedLink;
44
use OCA\Circles\Model\SharingFrame;
45
use OCP\Http\Client\IClientService;
46
47
48
class SharingFrameService {
49
50
	/** @var string */
51
	private $userId;
52
53
	/** @var ConfigService */
54
	private $configService;
55
56
	/** @var CirclesRequest */
57
	private $circlesRequest;
58
59
	/** @var FederatedLinksRequest */
60
	private $federatedLinksRequest;
61
62
	/** @var BroadcastService */
63
	private $broadcastService;
64
65
	/** @var FederatedLinkService */
66
	private $federatedLinkService;
67
68
	/** @var ClientService */
69
	private $clientService;
70
71
	/** @var MiscService */
72
	private $miscService;
73
74
75
	/**
76
	 * SharingFrameService constructor.
77
	 *
78
	 * @param string $UserId
79
	 * @param ConfigService $configService
80
	 * @param CirclesRequest $circlesRequest
81
	 * @param FederatedLinksRequest $federatedLinksRequest
82
	 * @param BroadcastService $broadcastService
83
	 * @param FederatedLinkService $federatedLinkService
84
	 * @param IClientService $clientService
85
	 * @param MiscService $miscService
86
	 */
87 View Code Duplication
	public function __construct(
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...
Coding Style Naming introduced by
The parameter $UserId is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
88
		$UserId,
89
		ConfigService $configService,
90
		CirclesRequest $circlesRequest,
91
		FederatedLinksRequest $federatedLinksRequest,
92
		BroadcastService $broadcastService,
93
		FederatedLinkService $federatedLinkService,
94
		IClientService $clientService,
95
		MiscService $miscService
96
	) {
97
		$this->userId = $UserId;
98
		$this->configService = $configService;
99
		$this->circlesRequest = $circlesRequest;
100
		$this->federatedLinksRequest = $federatedLinksRequest;
101
		$this->broadcastService = $broadcastService;
102
		$this->federatedLinkService = $federatedLinkService;
103
		$this->clientService = $clientService;
0 ignored issues
show
Documentation Bug introduced by
It seems like $clientService of type object<OCP\Http\Client\IClientService> is incompatible with the declared type object<OC\Http\Client\ClientService> of property $clientService.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
104
		$this->miscService = $miscService;
105
	}
106
107
108
	/**
109
	 * createFrame()
110
	 *
111
	 * Save the Frame containing the Payload.
112
	 * The Payload will be shared locally, and spread it live if a Broadcaster is set.
113
	 * Function will also initiate the federated broadcast to linked circles.
114
	 *
115
	 * @param string $circleUniqueId
116
	 * @param SharingFrame $frame
117
	 * @param string|null $broadcast
118
	 *
119
	 * @throws Exception
120
	 * @throws MemberDoesNotExistException
121
	 */
122 View Code Duplication
	public function createFrame($circleUniqueId, SharingFrame $frame, $broadcast = null) {
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...
123
124
		try {
125
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
126
			$circle->getHigherViewer()
127
				   ->hasToBeMember();
128
129
			$frame->setCircle($circle);
130
131
			$this->generateHeaders($frame, $circle, $broadcast);
132
			$this->circlesRequest->saveFrame($frame);
133
134
			$this->initiateShare($circle->getUniqueId(), $frame->getUniqueId());
135
		} catch (Exception $e) {
136
			throw $e;
137
		}
138
	}
139
140
141
	/**
142
	 * Generate Headers and few more thing like UniqueId and Author.
143
	 * Check if the source is NOT Circles.
144
	 *
145
	 * @param SharingFrame $frame
146
	 * @param Circle $circle
147
	 * @param $broadcast
148
	 */
149
	private function generateHeaders(SharingFrame $frame, Circle $circle, $broadcast) {
150
151
		try {
152
			$frame->cannotBeFromCircles();
153
154
			$frame->setAuthor($this->userId);
155
			$frame->setHeader('author', $this->userId);
156
			$frame->setHeader('circleName', $circle->getName());
157
			$frame->setHeader('circleUniqueId', $circle->getUniqueId());
158
			$frame->setHeader('broadcast', (string)$broadcast);
159
			$frame->generateUniqueId();
160
161
		} catch (Exception $e) {
162
			throw new $e;
163
		}
164
	}
165
166
	/**
167
	 * @param string $circleUniqueId
168
	 * @param string $frameUniqueId
169
	 *
170
	 * @return null|SharingFrame
171
	 * @throws SharingFrameAlreadyDeliveredException
172
	 * @throws SharingFrameDoesNotExistException
173
	 */
174
	public function getFrameFromUniqueId($circleUniqueId, $frameUniqueId) {
175
		if ($frameUniqueId === null || $frameUniqueId === '') {
176
			throw new SharingFrameDoesNotExistException('unknown_share');
177
		}
178
179
		try {
180
			$frame = $this->circlesRequest->getFrame($circleUniqueId, $frameUniqueId);
181
			if ($frame->getCloudId() !== null) {
182
				throw new SharingFrameAlreadyDeliveredException('share_already_delivered');
183
			}
184
		} catch (SharingFrameDoesNotExistException $e) {
185
			throw new SharingFrameDoesNotExistException('unknown_share');
186
		}
187
188
		return $frame;
189
	}
190
191
192
	/**
193
	 * @param string $token
194
	 * @param string $uniqueId
195
	 * @param SharingFrame $frame
196
	 *
197
	 * @return bool
198
	 * @throws Exception
199
	 */
200
	public function receiveFrame($token, $uniqueId, SharingFrame &$frame) {
201
		try {
202
			$link = $this->federatedLinksRequest->getLinkFromToken((string)$token, (string)$uniqueId);
203
		} catch (Exception $e) {
204
			throw $e;
205
		}
206
207
		try {
208
			$this->circlesRequest->getFrame($link->getCircleId(), $frame->getUniqueId());
209
			throw new SharingFrameAlreadyExistException('shares_is_already_known');
210
		} catch (SharingFrameDoesNotExistException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
211
		}
212
213
		try {
214
			$circle = $this->circlesRequest->forceGetCircle($link->getCircleId());
215
		} catch (CircleDoesNotExistException $e) {
216
			throw new CircleDoesNotExistException('unknown_circle');
217
		}
218
219
		$frame->setCircle($circle);
220
		$this->circlesRequest->saveFrame($frame);
221
222
		return true;
223
	}
224
225
226
	/**
227
	 * @param string $circleUniqueId
228
	 * @param string $frameUniqueId
229
	 *
230
	 * @return bool
231
	 * @throws Exception
232
	 */
233
	public function initiateShare($circleUniqueId, $frameUniqueId) {
234
		$args = [
235
			'circleId' => $circleUniqueId,
236
			'frameId'  => $frameUniqueId
237
		];
238
239
		$client = $this->clientService->newClient();
240
		try {
241
			$client->post(
242
				$this->generatePayloadDeliveryURL($this->configService->getLocalAddress()), [
243
																							  'body'            => $args,
244
																							  'timeout'         => 10,
245
																							  'connect_timeout' => 10,
246
																						  ]
247
			);
248
249
			return true;
250
		} catch (Exception $e) {
251
			throw $e;
252
		}
253
	}
254
255
256
	/**
257
	 * @param string $remote
258
	 *
259
	 * @return string
260
	 */
261
	private function generatePayloadDeliveryURL($remote) {
262
		return $this->configService->generateRemoteHost($remote) . Application::REMOTE_URL_PAYLOAD;
263
	}
264
265
266
	/**
267
	 * @param SharingFrame $frame
268
	 *
269
	 * @throws Exception
270
	 */
271
	public function forwardSharingFrame(SharingFrame $frame) {
272
273
		try {
274
			$circle = $this->circlesRequest->forceGetCircle(
275
				$frame->getCircle()
276
					  ->getUniqueId()
277
			);
278
		} catch (CircleDoesNotExistException $e) {
279
			throw new CircleDoesNotExistException('unknown_circle');
280
		}
281
282
		$links = $this->federatedLinksRequest->getLinksFromCircle(
283
			$frame->getCircle()
284
				  ->getUniqueId(), FederatedLink::STATUS_LINK_UP
285
		);
286
287
		$this->forwardSharingFrameToFederatedLinks($circle, $frame, $links);
288
	}
289
290
291
	/**
292
	 * @param Circle $circle
293
	 * @param SharingFrame $frame
294
	 * @param FederatedLink[] $links
295
	 */
296
	private function forwardSharingFrameToFederatedLinks(Circle $circle, SharingFrame $frame, $links) {
297
298
		$args = [
299
			'apiVersion' => Circles::version(),
300
			'uniqueId'   => $circle->getUniqueId(true),
301
			'item'       => json_encode($frame)
302
		];
303
304
		foreach ($links AS $link) {
305
			$args['token'] = $link->getToken(true);
306
			$this->deliverSharingFrameToLink($link, $args);
307
		}
308
	}
309
310
311
	/**
312
	 * sendRemoteShareToLinks();
313
	 *
314
	 * @param FederatedLink $link
315
	 * @param array $args
316
	 */
317
	private function deliverSharingFrameToLink($link, $args) {
318
319
		$client = $this->clientService->newClient();
320
		try {
321
			$request = $client->put(
322
				$this->generatePayloadDeliveryURL($link->getAddress()), [
323
																		  'body'            => $args,
324
																		  'timeout'         => 10,
325
																		  'connect_timeout' => 10,
326
																	  ]
327
			);
328
329
			$result = json_decode($request->getBody(), true);
330
			if ($result['status'] === -1) {
331
				throw new PayloadDeliveryException($result['reason']);
332
			}
333
334
		} catch (Exception $e) {
335
			$this->miscService->log(
336
				'fail to send frame to ' . $link->getAddress() . ' - ' . $e->getMessage()
337
			);
338
		}
339
	}
340
341
342
	/**
343
	 * @param SharingFrame $frame
344
	 */
345
	public function updateFrameWithCloudId(SharingFrame $frame) {
346
		$frame->setCloudId($this->configService->getLocalAddress());
347
		$this->circlesRequest->updateFrame($frame);
348
	}
349
350
351
}