Completed
Push — federated-circles ( 1e2c73...673aab )
by Maxence
02:33
created

FederatedController::receiveFederatedDelivery()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 32
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 6
nc 3
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\Controller;
28
29
use OC\AppFramework\Http;
30
use OCA\Circles\Model\FederatedLink;
31
use OCA\Circles\Model\SharingFrame;
32
use OCA\Circles\Service\FederatedService;
33
use OCA\Circles\Service\CirclesService;
34
use OCA\Circles\Service\ConfigService;
35
use OCA\Circles\Service\MembersService;
36
use OCA\Circles\Service\MiscService;
37
use OCA\Circles\Service\SharesService;
38
use OCP\AppFramework\Http\DataResponse;
39
use OCP\IL10N;
40
41
class FederatedController extends BaseController {
42
43
	/** @var string */
44
	protected $userId;
45
46
	/** @var IL10N */
47
	protected $l10n;
48
49
	/** @var ConfigService */
50
	protected $configService;
51
52
	/** @var CirclesService */
53
	protected $circlesService;
54
55
	/** @var MembersService */
56
	protected $membersService;
57
58
	/** @var SharesService */
59
	protected $sharesService;
60
61
	/** @var FederatedService */
62
	protected $federatedService;
63
64
	/** @var MiscService */
65
	protected $miscService;
66
67
68
	/**
69
	 * requestedLink()
70
	 *
71
	 * Called when a remote circle want to create a link.
72
	 * The function check if it is possible first; then create a link- object
73
	 * and sent it to be saved in the database.
74
	 *
75
	 * @PublicPage
76
	 * @NoCSRFRequired
77
	 *
78
	 * @param string $token
79
	 * @param string $uniqueId
80
	 * @param string $sourceName
81
	 * @param string $linkTo
82
	 * @param string $address
83
	 *
84
	 * @return DataResponse
85
	 */
86
	public function requestedLink($token, $uniqueId, $sourceName, $linkTo, $address) {
87
88
		if ($uniqueId === '' || !$this->configService->isFederatedAllowed()) {
89
			return $this->federatedFail('federated_not_allowed');
90
		}
91
92
		$circle = $this->circlesService->infoCircleByName($linkTo);
93
		if ($circle === null) {
94
			return $this->federatedFail('circle_does_not_exist');
95
		}
96
97
		if ($circle->getUniqueId() === $uniqueId) {
98
			return $this->federatedFail('duplicate_unique_id');
99
		}
100
101
		if ($this->federatedService->getLink($circle->getId(), $uniqueId) !== null) {
102
			return $this->federatedFail('duplicate_link');
103
		}
104
105
		$link = new FederatedLink();
106
		$link->setToken($token)
107
			 ->setUniqueId($uniqueId)
108
			 ->setRemoteCircleName($sourceName)
109
			 ->setAddress($address);
110
111
		if ($this->federatedService->initiateLink($circle, $link)) {
112
			return $this->federatedSuccess(
113
				['status' => $link->getStatus(), 'uniqueId' => $circle->getUniqueId()], $link
114
			);
115
		} else {
116
			return $this->federatedFail('link_failed');
117
		}
118
	}
119
120
121
	/**
122
	 * initFederatedDelivery()
123
	 *
124
	 * Note: this function will close the request mid-run from the client but will still
125
	 * running its process.
126
	 * Called by locally, the function will get the SharingFrame by its uniqueId from the database,
127
	 * assign him some Headers and will deliver it to each remotes linked to the circle the Payload
128
	 * belongs to. A status response is sent to free the client process before starting to
129
	 * broadcast the item to other federated links.
130
	 *
131
	 * @PublicPage
132
	 * @NoCSRFRequired
133
	 *
134
	 * @param $circleId
135
	 * @param $uniqueId
136
	 *
137
	 * @return DataResponse
0 ignored issues
show
Documentation introduced by
Should the return type not be DataResponse|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...
138
	 */
139
	public function initFederatedDelivery($circleId, $uniqueId) {
140
141
		if ($uniqueId === '' || !$this->configService->isFederatedAllowed()) {
142
			return $this->federatedFail('federated_not_allowed');
143
		}
144
145
		$frame = $this->sharesService->getFrameFromUniqueId($circleId, $uniqueId);
146
		if ($frame === null) {
147
			return $this->federatedFail('unknown_share');
148
		}
149
150
		if ($frame->getCloudId() !== null) {
151
			return $this->federatedFail('share_already_delivered');
152
		}
153
154
		// We don't want to keep the connection up
155
		$this->asyncAndLeaveClientOutOfThis('done');
156
157
		$this->federatedService->updateFrameWithCloudId($frame);
158
		$this->federatedService->sendRemoteShare($frame);
159
160
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method initFederatedDelivery() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
161
	}
162
163
164
	/**
165
	 * receiveFederatedDelivery()
166
	 *
167
	 * Note: this function will close the request mid-run from the client but will still
168
	 * running its process.
169
	 * Called by a remote circle to broadcast a Share item, the function will save the item
170
	 * in the database and broadcast it locally. A status response is sent to the remote to free
171
	 * the remote process before starting to broadcast the item to other federated links.
172
	 *
173
	 * @PublicPage
174
	 * @NoCSRFRequired
175
	 *
176
	 * @param $token
177
	 * @param $uniqueId
178
	 * @param $item
179
	 *
180
	 * @return DataResponse
0 ignored issues
show
Documentation introduced by
Should the return type not be DataResponse|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...
181
	 */
182
	public function receiveFederatedDelivery($token, $uniqueId, $item) {
183
184
		if ($uniqueId === '' || !$this->configService->isFederatedAllowed()) {
185
			return $this->federatedFail('federated_not_allowed');
186
		}
187
188
		$frame = SharingFrame::fromJSON($item);
189
		if (!$this->federatedService->receiveFrame($token, $uniqueId, $frame)) {
0 ignored issues
show
Bug introduced by
It seems like $frame defined by \OCA\Circles\Model\SharingFrame::fromJSON($item) on line 188 can be null; however, OCA\Circles\Service\Fede...Service::receiveFrame() 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...
190
			return $this->federatedFail('shares_is_already_known');
191
		}
192
193
194
		//	$this->sharesService->proceedFrame($token, $uniqueId$frame);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
195
		//$frame->setCircleId();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% 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...
196
		// TODO: SAVE THE FRAME, BROADCAST, DELIVER
197
//		$this->miscService->log(
198
//			"receiveFederatedDelivery start " . $token . '   ' . var_export($item, true) . '   '
199
//			. $uniqueId
200
//		);
201
		//	$this->miscService->log(
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
202
		//		'    ' . $item . '   '
203
		//		. $frame->getHeader('address')
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
204
		//	);
205
206
		// We don't want to keep the connection with the client up and running
207
		// as he might have others things to do
208
		//	$this->asyncAndLeaveClientOutOfThis('done');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
209
210
		//	sleep(15);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
211
		//	$this->miscService->log("receiveFederatedDelivery end");
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...
212
		//	exit();
213
	}
214
215
	/**
216
	 * Hacky way to async the rest of the process without keeping client on hold.
217
	 *
218
	 * @param string $result
219
	 */
220
	private function asyncAndLeaveClientOutOfThis($result = '') {
221
		if (ob_get_contents() !== false) {
222
			ob_end_clean();
223
		}
224
225
		header('Connection: close');
226
		ignore_user_abort();
227
		ob_start();
228
		echo($result);
229
		$size = ob_get_length();
230
		header('Content-Length: ' . $size);
231
		ob_end_flush();
232
		flush();
233
	}
234
235
	/**
236
	 * send a positive response to a request with an array of data, and confirm
237
	 * the identity of the link with a token
238
	 *
239
	 * @param array $data
240
	 * @param FederatedLink $link
241
	 *
242
	 * @return DataResponse
243
	 */
244
	private function federatedSuccess($data, $link) {
245
		return new DataResponse(
246
			array_merge($data, ['token' => $link->getToken()]), Http::STATUS_OK
247
		);
248
249
	}
250
251
	/**
252
	 * send a negative response to a request, with a reason of the failure.
253
	 *
254
	 * @param string $reason
255
	 *
256
	 * @return DataResponse
257
	 */
258
	private function federatedFail($reason) {
259
		return new DataResponse(
260
			[
261
				'status' => FederatedLink::STATUS_ERROR,
262
				'reason' => $reason
263
			],
264
			Http::STATUS_OK
265
		);
266
	}
267
}