Completed
Push — master ( 609978...e8817e )
by Morris
18:30
created

SharedMount::moveMount()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 4
nop 1
dl 0
loc 17
rs 9.7
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 Frédéric Fortier <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Vincent Petry <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OCA\Files_Sharing;
30
31
use OC\Cache\CappedMemoryCache;
32
use OC\Files\Filesystem;
33
use OC\Files\Mount\MountPoint;
34
use OC\Files\Mount\MoveableMount;
35
use OC\Files\View;
36
use OCP\Files\Storage\IStorageFactory;
37
38
/**
39
 * Shared mount points can be moved by the user
40
 */
41
class SharedMount extends MountPoint implements MoveableMount {
42
	/**
43
	 * @var \OCA\Files_Sharing\SharedStorage $storage
44
	 */
45
	protected $storage = null;
46
47
	/**
48
	 * @var \OC\Files\View
49
	 */
50
	private $recipientView;
51
52
	/**
53
	 * @var string
54
	 */
55
	private $user;
56
57
	/** @var \OCP\Share\IShare */
58
	private $superShare;
59
60
	/** @var \OCP\Share\IShare[] */
61
	private $groupedShares;
62
63
	/**
64
	 * @param string $storage
65
	 * @param SharedMount[] $mountpoints
66
	 * @param array $arguments
67
	 * @param IStorageFactory $loader
68
	 * @param View $recipientView
69
	 */
70
	public function __construct($storage, array $mountpoints, $arguments, IStorageFactory $loader, View $recipientView, CappedMemoryCache $folderExistCache) {
71
		$this->user = $arguments['user'];
72
		$this->recipientView = $recipientView;
73
74
		$this->superShare = $arguments['superShare'];
75
		$this->groupedShares = $arguments['groupedShares'];
76
77
		$newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints, $folderExistCache);
78
		$absMountPoint = '/' . $this->user . '/files' . $newMountPoint;
79
		parent::__construct($storage, $absMountPoint, $arguments, $loader);
80
	}
81
82
	/**
83
	 * check if the parent folder exists otherwise move the mount point up
84
	 *
85
	 * @param \OCP\Share\IShare $share
86
	 * @param SharedMount[] $mountpoints
87
	 * @return string
88
	 */
89
	private function verifyMountPoint(\OCP\Share\IShare $share, array $mountpoints, CappedMemoryCache $folderExistCache) {
90
91
		$mountPoint = basename($share->getTarget());
92
		$parent = dirname($share->getTarget());
93
94
		if ($folderExistCache->hasKey($parent)) {
95
			$parentExists = $folderExistCache->get($parent);
96
		} else {
97
			$parentExists = $this->recipientView->is_dir($parent);
98
			$folderExistCache->set($parent, $parentExists);
99
		}
100
		if (!$parentExists) {
101
			$parent = Helper::getShareFolder($this->recipientView);
102
		}
103
104
		$newMountPoint = $this->generateUniqueTarget(
105
			\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
106
			$this->recipientView,
107
			$mountpoints
108
		);
109
110
		if ($newMountPoint !== $share->getTarget()) {
111
			$this->updateFileTarget($newMountPoint, $share);
112
		}
113
114
		return $newMountPoint;
115
	}
116
117
	/**
118
	 * update fileTarget in the database if the mount point changed
119
	 *
120
	 * @param string $newPath
121
	 * @param \OCP\Share\IShare $share
122
	 * @return bool
123
	 */
124
	private function updateFileTarget($newPath, &$share) {
125
		$share->setTarget($newPath);
126
127
		foreach ($this->groupedShares as $tmpShare) {
128
			$tmpShare->setTarget($newPath);
129
			\OC::$server->getShareManager()->moveShare($tmpShare, $this->user);
130
		}
131
	}
132
133
134
	/**
135
	 * @param string $path
136
	 * @param View $view
137
	 * @param SharedMount[] $mountpoints
138
	 * @return mixed
139
	 */
140
	private function generateUniqueTarget($path, $view, array $mountpoints) {
141
		$pathinfo = pathinfo($path);
142
		$ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
143
		$name = $pathinfo['filename'];
144
		$dir = $pathinfo['dirname'];
145
146
		$i = 2;
147
		$absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
148
		while ($view->file_exists($path) || isset($mountpoints[$absolutePath])) {
149
			$path = Filesystem::normalizePath($dir . '/' . $name . ' (' . $i . ')' . $ext);
150
			$absolutePath = $this->recipientView->getAbsolutePath($path) . '/';
151
			$i++;
152
		}
153
154
		return $path;
155
	}
156
157
	/**
158
	 * Format a path to be relative to the /user/files/ directory
159
	 *
160
	 * @param string $path the absolute path
161
	 * @return string e.g. turns '/admin/files/test.txt' into '/test.txt'
162
	 * @throws \OCA\Files_Sharing\Exceptions\BrokenPath
163
	 */
164
	protected function stripUserFilesPath($path) {
165
		$trimmed = ltrim($path, '/');
166
		$split = explode('/', $trimmed);
167
168
		// it is not a file relative to data/user/files
169
		if (count($split) < 3 || $split[1] !== 'files') {
170
			\OC::$server->getLogger()->error('Can not strip userid and "files/" from path: ' . $path, ['app' => 'files_sharing']);
171
			throw new \OCA\Files_Sharing\Exceptions\BrokenPath('Path does not start with /user/files', 10);
172
		}
173
174
		// skip 'user' and 'files'
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
175
		$sliced = array_slice($split, 2);
176
		$relPath = implode('/', $sliced);
177
178
		return '/' . $relPath;
179
	}
180
181
	/**
182
	 * Move the mount point to $target
183
	 *
184
	 * @param string $target the target mount point
185
	 * @return bool
186
	 */
187
	public function moveMount($target) {
188
189
		$relTargetPath = $this->stripUserFilesPath($target);
190
		$share = $this->storage->getShare();
191
192
		$result = true;
193
194
		try {
195
			$this->updateFileTarget($relTargetPath, $share);
196
			$this->setMountPoint($target);
197
			$this->storage->setMountPoint($relTargetPath);
198
		} catch (\Exception $e) {
199
			\OC::$server->getLogger()->logException($e, ['app' => 'files_sharing', 'message' => 'Could not rename mount point for shared folder "' . $this->getMountPoint() . '" to "' . $target . '"']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

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...
200
		}
201
202
		return $result;
203
	}
204
205
	/**
206
	 * Remove the mount points
207
	 *
208
	 * @return bool
209
	 */
210
	public function removeMount() {
211
		$mountManager = \OC\Files\Filesystem::getMountManager();
212
		/** @var $storage \OCA\Files_Sharing\SharedStorage */
213
		$storage = $this->getStorage();
214
		$result = $storage->unshareStorage();
215
		$mountManager->removeMount($this->mountPoint);
216
217
		return $result;
218
	}
219
220
	/**
221
	 * @return \OCP\Share\IShare
222
	 */
223
	public function getShare() {
224
		return $this->superShare;
225
	}
226
227
	/**
228
	 * Get the file id of the root of the storage
229
	 *
230
	 * @return int
231
	 */
232
	public function getStorageRootId() {
233
		return $this->getShare()->getNodeId();
234
	}
235
236
	/**
237
	 * @return int
238
	 */
239
	public function getNumericStorageId() {
240
		if (!is_null($this->getShare()->getNodeCacheEntry())) {
241
			return $this->getShare()->getNodeCacheEntry()->getStorageId();
242
		} else {
243
			$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
244
245
			$query = $builder->select('storage')
246
				->from('filecache')
247
				->where($builder->expr()->eq('fileid', $builder->createNamedParameter($this->getStorageRootId())));
248
249
			$result = $query->execute();
250
			$row = $result->fetch();
251
			$result->closeCursor();
252
			if ($row) {
253
				return (int)$row['storage'];
254
			}
255
			return -1;
256
		}
257
	}
258
259
	public function getMountType() {
260
		return 'shared';
261
	}
262
}
263