Completed
Push — master ( 8b683f...7025f1 )
by Morris
29:10 queued 12:40
created

Notifications::sendRemoteShare()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 5
nop 8
dl 0
loc 34
rs 8.1315
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 *
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
26
namespace OCA\FederatedFileSharing;
27
28
use OCP\AppFramework\Http;
29
use OCP\BackgroundJob\IJobList;
30
use OCP\Federation\ICloudFederationFactory;
31
use OCP\Federation\ICloudFederationProviderManager;
32
use OCP\Http\Client\IClientService;
33
use OCP\OCS\IDiscoveryService;
34
35
class Notifications {
36
	const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
37
38
	/** @var AddressHandler */
39
	private $addressHandler;
40
41
	/** @var IClientService */
42
	private $httpClientService;
43
44
	/** @var IDiscoveryService */
45
	private $discoveryService;
46
47
	/** @var IJobList  */
48
	private $jobList;
49
50
	/** @var ICloudFederationProviderManager */
51
	private $federationProviderManager;
52
53
	/** @var ICloudFederationFactory */
54
	private $cloudFederationFactory;
55
56
	/**
57
	 * @param AddressHandler $addressHandler
58
	 * @param IClientService $httpClientService
59
	 * @param IDiscoveryService $discoveryService
60
	 * @param IJobList $jobList
61
	 * @param ICloudFederationProviderManager $federationProviderManager
62
	 * @param ICloudFederationFactory $cloudFederationFactory
63
	 */
64
	public function __construct(
65
		AddressHandler $addressHandler,
66
		IClientService $httpClientService,
67
		IDiscoveryService $discoveryService,
68
		IJobList $jobList,
69
		ICloudFederationProviderManager $federationProviderManager,
70
		ICloudFederationFactory $cloudFederationFactory
71
	) {
72
		$this->addressHandler = $addressHandler;
73
		$this->httpClientService = $httpClientService;
74
		$this->discoveryService = $discoveryService;
75
		$this->jobList = $jobList;
76
		$this->federationProviderManager = $federationProviderManager;
77
		$this->cloudFederationFactory = $cloudFederationFactory;
78
	}
79
80
	/**
81
	 * send server-to-server share to remote server
82
	 *
83
	 * @param string $token
84
	 * @param string $shareWith
85
	 * @param string $name
86
	 * @param int $remote_id
87
	 * @param string $owner
88
	 * @param string $ownerFederatedId
89
	 * @param string $sharedBy
90
	 * @param string $sharedByFederatedId
91
	 * @return bool
92
	 * @throws \OC\HintException
93
	 * @throws \OC\ServerNotAvailableException
94
	 */
95
	public function sendRemoteShare($token, $shareWith, $name, $remote_id, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId) {
96
97
		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
98
99
		if ($user && $remote) {
100
			$local = $this->addressHandler->generateRemoteURL();
101
102
			$fields = array(
103
				'shareWith' => $user,
104
				'token' => $token,
105
				'name' => $name,
106
				'remoteId' => $remote_id,
107
				'owner' => $owner,
108
				'ownerFederatedId' => $ownerFederatedId,
109
				'sharedBy' => $sharedBy,
110
				'sharedByFederatedId' => $sharedByFederatedId,
111
				'remote' => $local,
112
			);
113
114
			$result = $this->tryHttpPostToShareEndpoint($remote, '', $fields);
115
			$status = json_decode($result['result'], true);
116
117
			$ocsStatus = isset($status['ocs']);
118
			$ocsSuccess = $ocsStatus && ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200);
119
120
			if ($result['success'] && (!$ocsStatus ||$ocsSuccess)) {
121
				\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]);
122
				return true;
123
			}
124
125
		}
126
127
		return false;
128
	}
129
130
	/**
131
	 * ask owner to re-share the file with the given user
132
	 *
133
	 * @param string $token
134
	 * @param int $id remote Id
135
	 * @param int $shareId internal share Id
136
	 * @param string $remote remote address of the owner
137
	 * @param string $shareWith
138
	 * @param int $permission
139
	 * @param string $filename
140
	 * @return bool
141
	 * @throws \OC\HintException
142
	 * @throws \OC\ServerNotAvailableException
143
	 */
144
	public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission, $filename) {
145
146
		$fields = array(
147
			'shareWith' => $shareWith,
148
			'token' => $token,
149
			'permission' => $permission,
150
			'remoteId' => $shareId,
151
		);
152
153
		$ocmFields = $fields;
154
		$ocmFields['remoteId'] = $id;
155
		$ocmFields['localId'] = $shareId;
156
		$ocmFields['name'] = $filename;
157
158
		$ocmResult = $this->tryOCMEndPoint($remote, $ocmFields, 'reshare');
159
		if (is_array($ocmResult) && isset($ocmResult['token']) && isset($ocmResult['providerId'])) {
160
			return [$ocmResult['token'], $ocmResult['providerId']];
161
		}
162
163
		$result = $this->tryLegacyEndPoint(rtrim($remote, '/'), '/' . $id . '/reshare', $fields);
164
		$status = json_decode($result['result'], true);
165
166
		$httpRequestSuccessful = $result['success'];
167
		$ocsCallSuccessful = $status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200;
168
		$validToken = isset($status['ocs']['data']['token']) && is_string($status['ocs']['data']['token']);
169
		$validRemoteId = isset($status['ocs']['data']['remoteId']);
170
171
		if ($httpRequestSuccessful && $ocsCallSuccessful && $validToken && $validRemoteId) {
172
			return [
173
				$status['ocs']['data']['token'],
174
				(int)$status['ocs']['data']['remoteId']
175
			];
176
		}
177
178
		return false;
179
	}
180
181
	/**
182
	 * send server-to-server unshare to remote server
183
	 *
184
	 * @param string $remote url
185
	 * @param int $id share id
186
	 * @param string $token
187
	 * @return bool
188
	 */
189
	public function sendRemoteUnShare($remote, $id, $token) {
190
		$this->sendUpdateToRemote($remote, $id, $token, 'unshare');
191
	}
192
193
	/**
194
	 * send server-to-server unshare to remote server
195
	 *
196
	 * @param string $remote url
197
	 * @param int $id share id
198
	 * @param string $token
199
	 * @return bool
200
	 */
201
	public function sendRevokeShare($remote, $id, $token) {
202
		$this->sendUpdateToRemote($remote, $id, $token, 'reshare_undo');
203
	}
204
205
	/**
206
	 * send notification to remote server if the permissions was changed
207
	 *
208
	 * @param string $remote
209
	 * @param int $remoteId
210
	 * @param string $token
211
	 * @param int $permissions
212
	 * @return bool
213
	 */
214
	public function sendPermissionChange($remote, $remoteId, $token, $permissions) {
215
		$this->sendUpdateToRemote($remote, $remoteId, $token, 'permissions', ['permissions' => $permissions]);
216
	}
217
218
	/**
219
	 * forward accept reShare to remote server
220
	 *
221
	 * @param string $remote
222
	 * @param int $remoteId
223
	 * @param string $token
224
	 */
225
	public function sendAcceptShare($remote, $remoteId, $token) {
226
		$this->sendUpdateToRemote($remote, $remoteId, $token, 'accept');
227
	}
228
229
	/**
230
	 * forward decline reShare to remote server
231
	 *
232
	 * @param string $remote
233
	 * @param int $remoteId
234
	 * @param string $token
235
	 */
236
	public function sendDeclineShare($remote, $remoteId, $token) {
237
		$this->sendUpdateToRemote($remote, $remoteId, $token, 'decline');
238
	}
239
240
	/**
241
	 * inform remote server whether server-to-server share was accepted/declined
242
	 *
243
	 * @param string $remote
244
	 * @param string $token
245
	 * @param int $remoteId Share id on the remote host
246
	 * @param string $action possible actions: accept, decline, unshare, revoke, permissions
247
	 * @param array $data
248
	 * @param int $try
249
	 * @return boolean
250
	 */
251
	public function sendUpdateToRemote($remote, $remoteId, $token, $action, $data = [], $try = 0) {
252
253
		$fields = [
254
			'token' => $token,
255
			'remoteId' => $remoteId
256
			];
257
		foreach ($data as $key => $value) {
258
			$fields[$key] = $value;
259
		}
260
261
		$result = $this->tryHttpPostToShareEndpoint(rtrim($remote, '/'), '/' . $remoteId . '/' . $action, $fields, $action);
262
		$status = json_decode($result['result'], true);
263
264
		if ($result['success'] &&
265
			($status['ocs']['meta']['statuscode'] === 100 ||
266
				$status['ocs']['meta']['statuscode'] === 200
267
			)
268
		) {
269
			return true;
270
		} elseif ($try === 0) {
271
			// only add new job on first try
272
			$this->jobList->add('OCA\FederatedFileSharing\BackgroundJob\RetryJob',
273
				[
274
					'remote' => $remote,
275
					'remoteId' => $remoteId,
276
					'token' => $token,
277
					'action' => $action,
278
					'data' => json_encode($data),
279
					'try' => $try,
280
					'lastRun' => $this->getTimestamp()
281
				]
282
			);
283
		}
284
285
		return false;
286
	}
287
288
289
	/**
290
	 * return current timestamp
291
	 *
292
	 * @return int
293
	 */
294
	protected function getTimestamp() {
295
		return time();
296
	}
297
298
	/**
299
	 * try http post with the given protocol, if no protocol is given we pick
300
	 * the secure one (https)
301
	 *
302
	 * @param string $remoteDomain
303
	 * @param string $urlSuffix
304
	 * @param array $fields post parameters
305
	 * @param string $action define the action (possible values: share, reshare, accept, decline, unshare, revoke, permissions)
306
	 * @return array
307
	 * @throws \Exception
308
	 */
309
	protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields, $action="share") {
310
311
		if ($this->addressHandler->urlContainProtocol($remoteDomain) === false) {
312
			$remoteDomain = 'https://' . $remoteDomain;
313
		}
314
315
		$result = [
316
			'success' => false,
317
			'result' => '',
318
		];
319
320
		// if possible we use the new OCM API
321
		$ocmResult = $this->tryOCMEndPoint($remoteDomain, $fields, $action);
322
		if (is_array($ocmResult)) {
323
			$result['success'] = true;
324
			$result['result'] = json_encode([
325
				'ocs' => ['meta' => ['statuscode' => 200]]]);
326
			return $result;
327
		}
328
329
		return $this->tryLegacyEndPoint($remoteDomain, $urlSuffix, $fields);
330
	}
331
332
	/**
333
	 * try old federated sharing API if the OCM api doesn't work
334
	 *
335
	 * @param $remoteDomain
336
	 * @param $urlSuffix
337
	 * @param array $fields
338
	 * @return mixed
339
	 * @throws \Exception
340
	 */
341
	protected function tryLegacyEndPoint($remoteDomain, $urlSuffix, array $fields) {
342
343
		$result = [
344
			'success' => false,
345
			'result' => '',
346
		];
347
348
		// Fall back to old API
349
		$client = $this->httpClientService->newClient();
350
		$federationEndpoints = $this->discoveryService->discover($remoteDomain, 'FEDERATED_SHARING');
351
		$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
352
		try {
353
			$response = $client->post($remoteDomain . $endpoint . $urlSuffix . '?format=' . self::RESPONSE_FORMAT, [
354
				'body' => $fields,
355
				'timeout' => 10,
356
				'connect_timeout' => 10,
357
			]);
358
			$result['result'] = $response->getBody();
359
			$result['success'] = true;
360
		} catch (\Exception $e) {
361
			// if flat re-sharing is not supported by the remote server
362
			// we re-throw the exception and fall back to the old behaviour.
363
			// (flat re-shares has been introduced in Nextcloud 9.1)
364
			if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
365
				throw $e;
366
			}
367
		}
368
369
		return $result;
370
371
	}
372
373
	/**
374
	 * send action regarding federated sharing to the remote server using the OCM API
375
	 *
376
	 * @param $remoteDomain
377
	 * @param $fields
378
	 * @param $action
379
	 *
380
	 * @return bool
381
	 */
382
	protected function tryOCMEndPoint($remoteDomain, $fields, $action) {
383
		switch ($action) {
384
			case 'share':
385
				$share = $this->cloudFederationFactory->getCloudFederationShare(
386
					$fields['shareWith'] . '@' . $remoteDomain,
387
					$fields['name'],
388
					'',
389
					$fields['remoteId'],
390
					$fields['ownerFederatedId'],
391
					$fields['owner'],
392
					$fields['sharedByFederatedId'],
393
					$fields['sharedBy'],
394
					$fields['token'],
395
					'user',
396
					'file'
397
				);
398
				return $this->federationProviderManager->sendShare($share);
399
			case 'reshare':
400
				// ask owner to reshare a file
401
				$notification = $this->cloudFederationFactory->getCloudFederationNotification();
402
				$notification->setMessage('REQUEST_RESHARE',
403
					'file',
404
					$fields['remoteId'],
405
					[
406
						'sharedSecret' => $fields['token'],
407
						'shareWith' => $fields['shareWith'],
408
						'senderId' => $fields['localId'],
409
						'message' => 'Ask owner to reshare the file'
410
					]
411
				);
412
				return $this->federationProviderManager->sendNotification($remoteDomain, $notification);
413 View Code Duplication
			case 'unshare':
414
				//owner unshares the file from the recipient again
415
				$notification = $this->cloudFederationFactory->getCloudFederationNotification();
416
				$notification->setMessage('SHARE_UNSHARED',
417
					'file',
418
					$fields['remoteId'],
419
					[
420
						'sharedSecret' => $fields['token'],
421
						'messgage' => 'file is no longer shared with you'
422
					]
423
				);
424
				return $this->federationProviderManager->sendNotification($remoteDomain, $notification);
425 View Code Duplication
			case 'reshare_undo':
426
				// if a reshare was unshared we send the information to the initiator/owner
427
				$notification = $this->cloudFederationFactory->getCloudFederationNotification();
428
				$notification->setMessage('RESHARE_UNDO',
429
					'file',
430
					$fields['remoteId'],
431
					[
432
						'sharedSecret' => $fields['token'],
433
						'message' => 'reshare was revoked'
434
					]
435
				);
436
				return $this->federationProviderManager->sendNotification($remoteDomain, $notification);
437
		}
438
439
		return false;
440
441
	}
442
}
443