Completed
Push — master ( 7a269a...db1408 )
by Morris
13:03
created

Manager::getShares()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 4
nop 1
dl 0
loc 16
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\Files_Sharing\Helper;
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
			\OC_Util::setupFS($this->uid);
203
			$shareFolder = Helper::getShareFolder();
204
			$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
205
			$mountPoint = Filesystem::normalizePath($mountPoint);
206
			$hash = md5($mountPoint);
207
208
			$acceptShare = $this->connection->prepare('
209
				UPDATE `*PREFIX*share_external`
210
				SET `accepted` = ?,
211
					`mountpoint` = ?,
212
					`mountpoint_hash` = ?
213
				WHERE `id` = ? AND `user` = ?');
214
			$acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
215
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
216
217
			\OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $share['remote']]);
218
219
			$this->processNotification($id);
220
			return true;
221
		}
222
223
		return false;
224
	}
225
226
	/**
227
	 * decline server-to-server share
228
	 *
229
	 * @param int $id
230
	 * @return bool True if the share could be declined, false otherwise
231
	 */
232
	public function declineShare($id) {
233
234
		$share = $this->getShare($id);
235
236
		if ($share) {
237
			$removeShare = $this->connection->prepare('
238
				DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
239
			$removeShare->execute(array($id, $this->uid));
240
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
241
242
			$this->processNotification($id);
243
			return true;
244
		}
245
246
		return false;
247
	}
248
249
	/**
250
	 * @param int $remoteShare
251
	 */
252
	public function processNotification($remoteShare) {
253
		$filter = $this->notificationManager->createNotification();
254
		$filter->setApp('files_sharing')
255
			->setUser($this->uid)
256
			->setObject('remote_share', (int) $remoteShare);
257
		$this->notificationManager->markProcessed($filter);
258
	}
259
260
	/**
261
	 * inform remote server whether server-to-server share was accepted/declined
262
	 *
263
	 * @param string $remote
264
	 * @param string $token
265
	 * @param int $remoteId Share id on the remote host
266
	 * @param string $feedback
267
	 * @return boolean
268
	 */
269
	private function sendFeedbackToRemote($remote, $token, $remoteId, $feedback) {
270
271
		$federationEndpoints = $this->discoveryService->discover($remote, 'FEDERATED_SHARING');
272
		$endpoint = isset($federationEndpoints['share']) ? $federationEndpoints['share'] : '/ocs/v2.php/cloud/shares';
273
274
		$url = rtrim($remote, '/') . $endpoint . '/' . $remoteId . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT;
275
		$fields = array('token' => $token);
276
277
		$client = $this->clientService->newClient();
278
279
		try {
280
			$response = $client->post(
281
				$url,
282
				[
283
					'body' => $fields,
284
					'connect_timeout' => 10,
285
				]
286
			);
287
		} catch (\Exception $e) {
288
			return false;
289
		}
290
291
		$status = json_decode($response->getBody(), true);
292
293
		return ($status['ocs']['meta']['statuscode'] === 100 || $status['ocs']['meta']['statuscode'] === 200);
294
	}
295
296
	/**
297
	 * remove '/user/files' from the path and trailing slashes
298
	 *
299
	 * @param string $path
300
	 * @return string
301
	 */
302
	protected function stripPath($path) {
303
		$prefix = '/' . $this->uid . '/files';
304
		return rtrim(substr($path, strlen($prefix)), '/');
305
	}
306
307
	public function getMount($data) {
308
		$data['manager'] = $this;
309
		$mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
310
		$data['mountpoint'] = $mountPoint;
311
		$data['certificateManager'] = \OC::$server->getCertificateManager($this->uid);
312
		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...
313
	}
314
315
	/**
316
	 * @param array $data
317
	 * @return Mount
318
	 */
319
	protected function mountShare($data) {
320
		$mount = $this->getMount($data);
321
		$this->mountManager->addMount($mount);
322
		return $mount;
323
	}
324
325
	/**
326
	 * @return \OC\Files\Mount\Manager
327
	 */
328
	public function getMountManager() {
329
		return $this->mountManager;
330
	}
331
332
	/**
333
	 * @param string $source
334
	 * @param string $target
335
	 * @return bool
336
	 */
337
	public function setMountPoint($source, $target) {
338
		$source = $this->stripPath($source);
339
		$target = $this->stripPath($target);
340
		$sourceHash = md5($source);
341
		$targetHash = md5($target);
342
343
		$query = $this->connection->prepare('
344
			UPDATE `*PREFIX*share_external`
345
			SET `mountpoint` = ?, `mountpoint_hash` = ?
346
			WHERE `mountpoint_hash` = ?
347
			AND `user` = ?
348
		');
349
		$result = (bool)$query->execute(array($target, $targetHash, $sourceHash, $this->uid));
350
351
		return $result;
352
	}
353
354
	public function removeShare($mountPoint) {
355
356
		$mountPointObj = $this->mountManager->find($mountPoint);
357
		$id = $mountPointObj->getStorage()->getCache()->getId('');
358
359
		$mountPoint = $this->stripPath($mountPoint);
360
		$hash = md5($mountPoint);
361
362
		$getShare = $this->connection->prepare('
363
			SELECT `remote`, `share_token`, `remote_id`
364
			FROM  `*PREFIX*share_external`
365
			WHERE `mountpoint_hash` = ? AND `user` = ?');
366
		$result = $getShare->execute(array($hash, $this->uid));
367
368 View Code Duplication
		if ($result) {
369
			$share = $getShare->fetch();
370
			$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
371
		}
372
		$getShare->closeCursor();
373
374
		$query = $this->connection->prepare('
375
			DELETE FROM `*PREFIX*share_external`
376
			WHERE `mountpoint_hash` = ?
377
			AND `user` = ?
378
		');
379
		$result = (bool)$query->execute(array($hash, $this->uid));
380
381
		if($result) {
382
			$this->removeReShares($id);
383
		}
384
385
		return $result;
386
	}
387
388
	/**
389
	 * remove re-shares from share table and mapping in the federated_reshares table
390
	 *
391
	 * @param $mountPointId
392
	 */
393
	protected function removeReShares($mountPointId) {
394
		$selectQuery = $this->connection->getQueryBuilder();
395
		$query = $this->connection->getQueryBuilder();
396
		$selectQuery->select('id')->from('share')
397
			->where($selectQuery->expr()->eq('file_source', $query->createNamedParameter($mountPointId)));
398
		$select = $selectQuery->getSQL();
399
400
401
		$query->delete('federated_reshares')
402
			->where($query->expr()->in('share_id', $query->createFunction('(' . $select . ')')));
403
		$query->execute();
404
405
		$deleteReShares = $this->connection->getQueryBuilder();
406
		$deleteReShares->delete('share')
407
			->where($deleteReShares->expr()->eq('file_source', $deleteReShares->createNamedParameter($mountPointId)));
408
		$deleteReShares->execute();
409
	}
410
411
	/**
412
	 * remove all shares for user $uid if the user was deleted
413
	 *
414
	 * @param string $uid
415
	 * @return bool
416
	 */
417
	public function removeUserShares($uid) {
418
		$getShare = $this->connection->prepare('
419
			SELECT `remote`, `share_token`, `remote_id`
420
			FROM  `*PREFIX*share_external`
421
			WHERE `user` = ?');
422
		$result = $getShare->execute(array($uid));
423
424 View Code Duplication
		if ($result) {
425
			$shares = $getShare->fetchAll();
426
			foreach($shares as $share) {
427
				$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
428
			}
429
		}
430
431
		$query = $this->connection->prepare('
432
			DELETE FROM `*PREFIX*share_external`
433
			WHERE `user` = ?
434
		');
435
		return (bool)$query->execute(array($uid));
436
	}
437
438
	/**
439
	 * return a list of shares which are not yet accepted by the user
440
	 *
441
	 * @return array list of open server-to-server shares
442
	 */
443
	public function getOpenShares() {
444
		return $this->getShares(false);
445
	}
446
447
	/**
448
	 * return a list of shares which are accepted by the user
449
	 *
450
	 * @return array list of accepted server-to-server shares
451
	 */
452
	public function getAcceptedShares() {
453
		return $this->getShares(true);
454
	}
455
456
	/**
457
	 * return a list of shares for the user
458
	 *
459
	 * @param bool|null $accepted True for accepted only,
460
	 *                            false for not accepted,
461
	 *                            null for all shares of the user
462
	 * @return array list of open server-to-server shares
463
	 */
464
	private function getShares($accepted) {
465
		$query = 'SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`
466
		          FROM `*PREFIX*share_external` 
467
				  WHERE `user` = ?';
468
		$parameters = [$this->uid];
469
		if (!is_null($accepted)) {
470
			$query .= ' AND `accepted` = ?';
471
			$parameters[] = (int) $accepted;
472
		}
473
		$query .= ' ORDER BY `id` ASC';
474
475
		$shares = $this->connection->prepare($query);
476
		$result = $shares->execute($parameters);
477
478
		return $result ? $shares->fetchAll() : [];
479
	}
480
}
481