Completed
Push — master ( dccb89...31024b )
by Morris
12:39
created

Manager::mountShare()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Björn Schießle <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Stefan Weil <[email protected]>
13
 *
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OCA\Files_Sharing\External;
31
32
use OC\Files\Filesystem;
33
use OCA\FederatedFileSharing\DiscoveryManager;
34
use OCP\Files;
35
use OCP\Files\Storage\IStorageFactory;
36
use OCP\Http\Client\IClientService;
37
use OCP\IDBConnection;
38
use OCP\Notification\IManager;
39
use OCP\OCS\IDiscoveryService;
40
41
class Manager {
42
	const STORAGE = '\OCA\Files_Sharing\External\Storage';
43
44
	/**
45
	 * @var string
46
	 */
47
	private $uid;
48
49
	/**
50
	 * @var IDBConnection
51
	 */
52
	private $connection;
53
54
	/**
55
	 * @var \OC\Files\Mount\Manager
56
	 */
57
	private $mountManager;
58
59
	/**
60
	 * @var IStorageFactory
61
	 */
62
	private $storageLoader;
63
64
	/**
65
	 * @var IClientService
66
	 */
67
	private $clientService;
68
69
	/**
70
	 * @var IManager
71
	 */
72
	private $notificationManager;
73
74
	/**
75
	 * @var IDiscoveryService
76
	 */
77
	private $discoveryService;
78
79
	/**
80
	 * @param IDBConnection $connection
81
	 * @param \OC\Files\Mount\Manager $mountManager
82
	 * @param IStorageFactory $storageLoader
83
	 * @param IClientService $clientService
84
	 * @param IManager $notificationManager
85
	 * @param IDiscoveryService $discoveryService
86
	 * @param string $uid
87
	 */
88
	public function __construct(IDBConnection $connection,
89
								\OC\Files\Mount\Manager $mountManager,
90
								IStorageFactory $storageLoader,
91
								IClientService $clientService,
92
								IManager $notificationManager,
93
								IDiscoveryService $discoveryService,
94
								$uid) {
95
		$this->connection = $connection;
96
		$this->mountManager = $mountManager;
97
		$this->storageLoader = $storageLoader;
98
		$this->clientService = $clientService;
99
		$this->uid = $uid;
100
		$this->notificationManager = $notificationManager;
101
		$this->discoveryService = $discoveryService;
102
	}
103
104
	/**
105
	 * add new server-to-server share
106
	 *
107
	 * @param string $remote
108
	 * @param string $token
109
	 * @param string $password
110
	 * @param string $name
111
	 * @param string $owner
112
	 * @param boolean $accepted
113
	 * @param string $user
114
	 * @param int $remoteId
115
	 * @return Mount|null
116
	 */
117
	public function addShare($remote, $token, $password, $name, $owner, $accepted=false, $user = null, $remoteId = -1) {
118
119
		$user = $user ? $user : $this->uid;
120
		$accepted = $accepted ? 1 : 0;
121
		$name = Filesystem::normalizePath('/' . $name);
122
123
		if (!$accepted) {
124
			// To avoid conflicts with the mount point generation later,
125
			// we only use a temporary mount point name here. The real
126
			// mount point name will be generated when accepting the share,
127
			// using the original share item name.
128
			$tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}';
129
			$mountPoint = $tmpMountPointName;
130
			$hash = md5($tmpMountPointName);
131
			$data = [
132
				'remote'		=> $remote,
133
				'share_token'	=> $token,
134
				'password'		=> $password,
135
				'name'			=> $name,
136
				'owner'			=> $owner,
137
				'user'			=> $user,
138
				'mountpoint'	=> $mountPoint,
139
				'mountpoint_hash'	=> $hash,
140
				'accepted'		=> $accepted,
141
				'remote_id'		=> $remoteId,
142
			];
143
144
			$i = 1;
145
			while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) {
146
				// The external share already exists for the user
147
				$data['mountpoint'] = $tmpMountPointName . '-' . $i;
148
				$data['mountpoint_hash'] = md5($data['mountpoint']);
149
				$i++;
150
			}
151
			return null;
152
		}
153
154
		$mountPoint = Files::buildNotExistingFileName('/', $name);
155
		$mountPoint = Filesystem::normalizePath('/' . $mountPoint);
156
		$hash = md5($mountPoint);
157
158
		$query = $this->connection->prepare('
159
				INSERT INTO `*PREFIX*share_external`
160
					(`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`)
161
				VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
162
			');
163
		$query->execute(array($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId));
164
165
		$options = array(
166
			'remote'	=> $remote,
167
			'token'		=> $token,
168
			'password'	=> $password,
169
			'mountpoint'	=> $mountPoint,
170
			'owner'		=> $owner
171
		);
172
		return $this->mountShare($options);
173
	}
174
175
	/**
176
	 * get share
177
	 *
178
	 * @param int $id share id
179
	 * @return mixed share of false
180
	 */
181
	public function getShare($id) {
182
		$getShare = $this->connection->prepare('
183
			SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`
184
			FROM  `*PREFIX*share_external`
185
			WHERE `id` = ? AND `user` = ?');
186
		$result = $getShare->execute(array($id, $this->uid));
187
188
		return $result ? $getShare->fetch() : false;
189
	}
190
191
	/**
192
	 * accept server-to-server share
193
	 *
194
	 * @param int $id
195
	 * @return bool True if the share could be accepted, false otherwise
196
	 */
197
	public function acceptShare($id) {
198
199
		$share = $this->getShare($id);
200
201
		if ($share) {
202
			$mountPoint = Files::buildNotExistingFileName('/', $share['name']);
203
			$mountPoint = Filesystem::normalizePath('/' . $mountPoint);
204
			$hash = md5($mountPoint);
205
206
			$acceptShare = $this->connection->prepare('
207
				UPDATE `*PREFIX*share_external`
208
				SET `accepted` = ?,
209
					`mountpoint` = ?,
210
					`mountpoint_hash` = ?
211
				WHERE `id` = ? AND `user` = ?');
212
			$acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
213
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
214
215
			\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $share['remote']]);
216
217
			$this->processNotification($id);
218
			return true;
219
		}
220
221
		return false;
222
	}
223
224
	/**
225
	 * decline server-to-server share
226
	 *
227
	 * @param int $id
228
	 * @return bool True if the share could be declined, false otherwise
229
	 */
230
	public function declineShare($id) {
231
232
		$share = $this->getShare($id);
233
234
		if ($share) {
235
			$removeShare = $this->connection->prepare('
236
				DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
237
			$removeShare->execute(array($id, $this->uid));
238
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
239
240
			$this->processNotification($id);
241
			return true;
242
		}
243
244
		return false;
245
	}
246
247
	/**
248
	 * @param int $remoteShare
249
	 */
250
	public function processNotification($remoteShare) {
251
		$filter = $this->notificationManager->createNotification();
252
		$filter->setApp('files_sharing')
253
			->setUser($this->uid)
254
			->setObject('remote_share', (int) $remoteShare);
255
		$this->notificationManager->markProcessed($filter);
256
	}
257
258
	/**
259
	 * inform remote server whether server-to-server share was accepted/declined
260
	 *
261
	 * @param string $remote
262
	 * @param string $token
263
	 * @param int $remoteId Share id on the remote host
264
	 * @param string $feedback
265
	 * @return boolean
266
	 */
267
	private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) {
268
269
		$federationEndpoints = $this->discoveryService->discover($remote, 'FEDERATED_SHARING');
270
		$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
271
272
		$url = rtrim($remote, '/') . $endpoint . '/' . $remoteId . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT;
273
		$fields = array('token' => $token);
274
275
		$client = $this->clientService->newClient();
276
277
		try {
278
			$response = $client->post(
279
				$url,
280
				[
281
					'body' => $fields,
282
					'connect_timeout' => 10,
283
				]
284
			);
285
		} catch (\Exception $e) {
286
			return false;
287
		}
288
289
		$status = json_decode($response->getBody(), true);
290
291
		return ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200);
292
	}
293
294
	/**
295
	 * remove '/user/files' from the path and trailing slashes
296
	 *
297
	 * @param string $path
298
	 * @return string
299
	 */
300
	protected function stripPath($path) {
301
		$prefix = '/' . $this->uid . '/files';
302
		return rtrim(substr($path, strlen($prefix)), '/');
303
	}
304
305
	public function getMount($data) {
306
		$data['manager'] = $this;
307
		$mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
308
		$data['mountpoint'] = $mountPoint;
309
		$data['certificateManager'] = \OC::$server->getCertificateManager($this->uid);
310
		return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
0 ignored issues
show
Documentation introduced by
$this->storageLoader is of type object<OCP\Files\Storage\IStorageFactory>, but the function expects a object<OC\Files\Storage\StorageFactory>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
311
	}
312
313
	/**
314
	 * @param array $data
315
	 * @return Mount
316
	 */
317
	protected function mountShare($data) {
318
		$mount = $this->getMount($data);
319
		$this->mountManager->addMount($mount);
320
		return $mount;
321
	}
322
323
	/**
324
	 * @return \OC\Files\Mount\Manager
325
	 */
326
	public function getMountManager() {
327
		return $this->mountManager;
328
	}
329
330
	/**
331
	 * @param string $source
332
	 * @param string $target
333
	 * @return bool
334
	 */
335
	public function setMountPoint($source, $target) {
336
		$source = $this->stripPath($source);
337
		$target = $this->stripPath($target);
338
		$sourceHash = md5($source);
339
		$targetHash = md5($target);
340
341
		$query = $this->connection->prepare('
342
			UPDATE `*PREFIX*share_external`
343
			SET `mountpoint` = ?, `mountpoint_hash` = ?
344
			WHERE `mountpoint_hash` = ?
345
			AND `user` = ?
346
		');
347
		$result = (bool)$query->execute(array($target, $targetHash, $sourceHash, $this->uid));
348
349
		return $result;
350
	}
351
352
	public function removeShare($mountPoint) {
353
354
		$mountPointObj = $this->mountManager->find($mountPoint);
355
		$id = $mountPointObj->getStorage()->getCache()->getId('');
356
357
		$mountPoint = $this->stripPath($mountPoint);
358
		$hash = md5($mountPoint);
359
360
		$getShare = $this->connection->prepare('
361
			SELECT `remote`, `share_token`, `remote_id`
362
			FROM  `*PREFIX*share_external`
363
			WHERE `mountpoint_hash` = ? AND `user` = ?');
364
		$result = $getShare->execute(array($hash, $this->uid));
365
366 View Code Duplication
		if ($result) {
367
			$share = $getShare->fetch();
368
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
369
		}
370
		$getShare->closeCursor();
371
372
		$query = $this->connection->prepare('
373
			DELETE FROM `*PREFIX*share_external`
374
			WHERE `mountpoint_hash` = ?
375
			AND `user` = ?
376
		');
377
		$result = (bool)$query->execute(array($hash, $this->uid));
378
379
		if($result) {
380
			$this->removeReShares($id);
381
		}
382
383
		return $result;
384
	}
385
386
	/**
387
	 * remove re-shares from share table and mapping in the federated_reshares table
388
	 *
389
	 * @param $mountPointId
390
	 */
391
	protected function removeReShares($mountPointId) {
392
		$selectQuery = $this->connection->getQueryBuilder();
393
		$query = $this->connection->getQueryBuilder();
394
		$selectQuery->select('id')->from('share')
395
			->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId)));
396
		$select = $selectQuery->getSQL();
397
398
399
		$query->delete('federated_reshares')
400
			->where($query->expr()->in('share_id', $query->createFunction('(' . $select . ')')));
401
		$query->execute();
402
403
		$deleteReShares = $this->connection->getQueryBuilder();
404
		$deleteReShares->delete('share')
405
			->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId)));
406
		$deleteReShares->execute();
407
	}
408
409
	/**
410
	 * remove all shares for user $uid if the user was deleted
411
	 *
412
	 * @param string $uid
413
	 * @return bool
414
	 */
415
	public function removeUserShares($uid) {
416
		$getShare = $this->connection->prepare('
417
			SELECT `remote`, `share_token`, `remote_id`
418
			FROM  `*PREFIX*share_external`
419
			WHERE `user` = ?');
420
		$result = $getShare->execute(array($uid));
421
422 View Code Duplication
		if ($result) {
423
			$shares = $getShare->fetchAll();
424
			foreach($shares as $share) {
425
				$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
426
			}
427
		}
428
429
		$query = $this->connection->prepare('
430
			DELETE FROM `*PREFIX*share_external`
431
			WHERE `user` = ?
432
		');
433
		return (bool)$query->execute(array($uid));
434
	}
435
436
	/**
437
	 * return a list of shares which are not yet accepted by the user
438
	 *
439
	 * @return array list of open server-to-server shares
440
	 */
441
	public function getOpenShares() {
442
		return $this->getShares(false);
443
	}
444
445
	/**
446
	 * return a list of shares which are accepted by the user
447
	 *
448
	 * @return array list of accepted server-to-server shares
449
	 */
450
	public function getAcceptedShares() {
451
		return $this->getShares(true);
452
	}
453
454
	/**
455
	 * return a list of shares for the user
456
	 *
457
	 * @param bool|null $accepted True for accepted only,
458
	 *                            false for not accepted,
459
	 *                            null for all shares of the user
460
	 * @return array list of open server-to-server shares
461
	 */
462
	private function getShares($accepted) {
463
		$query = 'SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`
464
		          FROM `*PREFIX*share_external` 
465
				  WHERE `user` = ?';
466
		$parameters = [$this->uid];
467
		if (!is_null($accepted)) {
468
			$query .= ' AND `accepted` = ?';
469
			$parameters[] = (int) $accepted;
470
		}
471
		$query .= ' ORDER BY `id` ASC';
472
473
		$shares = $this->connection->prepare($query);
474
		$result = $shares->execute($parameters);
475
476
		return $result ? $shares->fetchAll() : [];
477
	}
478
}
479