1<?php
2/**
3 * @author Bernhard Posselt <dev@bernhard-posselt.com>
4 * @author Joas Schilling <coding@schilljs.com>
5 * @author Jörn Friedrich Dreyer <jfd@butonic.de>
6 * @author Morris Jobke <hey@morrisjobke.de>
7 * @author Robin Appelman <icewind@owncloud.com>
8 * @author Roeland Jago Douma <rullzer@owncloud.com>
9 * @author Stefan Weil <sw@weilnetz.de>
10 * @author Thomas Müller <thomas.mueller@tmit.eu>
11 * @author Vincent Petry <pvince81@owncloud.com>
12 *
13 * @copyright Copyright (c) 2018, ownCloud GmbH
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
30namespace OC\Files\Node;
31
32use OC\Files\Meta\MetaRootNode;
33use OC\Files\Mount\MountPoint;
34use OC\User\NoUserException;
35use OCP\Constants;
36use OCP\Files\NotFoundException;
37use OCP\Files\NotPermittedException;
38use OC\Hooks\PublicEmitter;
39use OCP\Files\IRootFolder;
40
41/**
42 * Class Root
43 *
44 * Hooks available in scope \OC\Files
45 * - preWrite(\OCP\Files\Node $node)
46 * - postWrite(\OCP\Files\Node $node)
47 * - preCreate(\OCP\Files\Node $node)
48 * - postCreate(\OCP\Files\Node $node)
49 * - preDelete(\OCP\Files\Node $node)
50 * - postDelete(\OCP\Files\Node $node)
51 * - preTouch(\OC\FilesP\Node $node, int $mtime)
52 * - postTouch(\OCP\Files\Node $node)
53 * - preCopy(\OCP\Files\Node $source, \OCP\Files\Node $target)
54 * - postCopy(\OCP\Files\Node $source, \OCP\Files\Node $target)
55 * - preRename(\OCP\Files\Node $source, \OCP\Files\Node $target)
56 * - postRename(\OCP\Files\Node $source, \OCP\Files\Node $target)
57 *
58 * @package OC\Files\Node
59 */
60class Root extends Folder implements IRootFolder {
61
62	/**
63	 * @var \OC\Files\Mount\Manager $mountManager
64	 */
65	private $mountManager;
66
67	/**
68	 * @var \OC\Hooks\PublicEmitter
69	 */
70	private $emitter;
71
72	/**
73	 * @var \OC\User\User $user
74	 */
75	private $user;
76
77	/**
78	 * @param \OC\Files\Mount\Manager $manager
79	 * @param \OC\Files\View $view
80	 * @param \OC\User\User|null $user
81	 */
82	public function __construct($manager, $view, $user) {
83		parent::__construct($this, $view, '');
84		$this->mountManager = $manager;
85		$this->user = $user;
86		$this->emitter = new PublicEmitter();
87	}
88
89	/**
90	 * Get the user for which the filesystem is setup
91	 *
92	 * @return \OC\User\User
93	 */
94	public function getUser() {
95		return $this->user;
96	}
97
98	/**
99	 * @param string $scope
100	 * @param string $method
101	 * @param callable $callback
102	 */
103	public function listen($scope, $method, callable $callback) {
104		$this->emitter->listen($scope, $method, $callback);
105	}
106
107	/**
108	 * @param string $scope optional
109	 * @param string $method optional
110	 * @param callable $callback optional
111	 */
112	public function removeListener($scope = null, $method = null, callable $callback = null) {
113		$this->emitter->removeListener($scope, $method, $callback);
114	}
115
116	/**
117	 * @param string $scope
118	 * @param string $method
119	 * @param Node[] $arguments
120	 */
121	public function emit($scope, $method, $arguments = []) {
122		$this->emitter->emit($scope, $method, $arguments);
123	}
124
125	/**
126	 * @param \OC\Files\Storage\Storage $storage
127	 * @param string $mountPoint
128	 * @param array $arguments
129	 */
130	public function mount($storage, $mountPoint, $arguments = []) {
131		$mount = new MountPoint($storage, $mountPoint, $arguments);
132		$this->mountManager->addMount($mount);
133	}
134
135	/**
136	 * @param string $mountPoint
137	 * @return \OC\Files\Mount\MountPoint
138	 */
139	public function getMount($mountPoint) {
140		return $this->mountManager->find($mountPoint);
141	}
142
143	/**
144	 * @param string $mountPoint
145	 * @return \OC\Files\Mount\MountPoint[]
146	 */
147	public function getMountsIn($mountPoint) {
148		return $this->mountManager->findIn($mountPoint);
149	}
150
151	/**
152	 * @param string $storageId
153	 * @return \OC\Files\Mount\MountPoint[]
154	 */
155	public function getMountByStorageId($storageId) {
156		return $this->mountManager->findByStorageId($storageId);
157	}
158
159	/**
160	 * @param int $numericId
161	 * @return MountPoint[]
162	 */
163	public function getMountByNumericStorageId($numericId) {
164		return $this->mountManager->findByNumericId($numericId);
165	}
166
167	/**
168	 * @param \OC\Files\Mount\MountPoint $mount
169	 */
170	public function unMount($mount) {
171		// ToDo: why doesn't this call removeMount ?
172		/* @phan-suppress-next-line PhanUndeclaredMethod */
173		$this->mountManager->remove($mount);
174	}
175
176	/**
177	 * @param string $path
178	 * @throws \OCP\Files\NotFoundException
179	 * @throws \OCP\Files\NotPermittedException
180	 * @return File|Folder
181	 */
182	public function get($path) {
183		$path = $this->normalizePath($path);
184		if ($this->isValidPath($path)) {
185			$fullPath = $this->getFullPath($path);
186			$virtualNode = $this->resolveVirtualNode($fullPath);
187			if ($virtualNode !== null) {
188				return $virtualNode;
189			}
190			$fileExists = $this->view->file_exists($fullPath);
191			if ($fileExists) {
192				return $this->createNode($fullPath);
193			} else {
194				// try getting the fileinfo in case we have data in the filecache
195				$fileInfo = $this->view->getFileInfo($fullPath);
196				if (!$fileInfo) {
197					throw new NotFoundException($path);
198				}
199				return $this->createNode($fullPath, $fileInfo);
200			}
201		} else {
202			throw new NotPermittedException();
203		}
204	}
205
206	//most operations can't be done on the root
207
208	/**
209	 * @param string $targetPath
210	 * @throws \OCP\Files\NotPermittedException
211	 * @return \OC\Files\Node\Node
212	 */
213	public function rename($targetPath) {
214		throw new NotPermittedException();
215	}
216
217	public function delete() {
218		throw new NotPermittedException();
219	}
220
221	/**
222	 * @param string $targetPath
223	 * @throws \OCP\Files\NotPermittedException
224	 * @return \OC\Files\Node\Node
225	 */
226	public function copy($targetPath) {
227		throw new NotPermittedException();
228	}
229
230	/**
231	 * @param int $mtime
232	 * @throws \OCP\Files\NotPermittedException
233	 */
234	public function touch($mtime = null) {
235		throw new NotPermittedException();
236	}
237
238	/**
239	 * @return \OC\Files\Storage\Storage
240	 * @throws \OCP\Files\NotFoundException
241	 */
242	public function getStorage() {
243		throw new NotFoundException();
244	}
245
246	/**
247	 * @return string
248	 */
249	public function getPath() {
250		return '/';
251	}
252
253	/**
254	 * @return string
255	 */
256	public function getInternalPath() {
257		return '';
258	}
259
260	/**
261	 * @return int
262	 */
263	public function getId() {
264		return null;
265	}
266
267	/**
268	 * @return array
269	 */
270	public function stat() {
271		return null;
272	}
273
274	/**
275	 * @return int
276	 */
277	public function getMTime() {
278		return null;
279	}
280
281	/**
282	 * @return int
283	 */
284	public function getSize() {
285		return null;
286	}
287
288	/**
289	 * @return string
290	 */
291	public function getEtag() {
292		return null;
293	}
294
295	/**
296	 * @return int
297	 */
298	public function getPermissions() {
299		return Constants::PERMISSION_CREATE;
300	}
301
302	/**
303	 * @return bool
304	 */
305	public function isReadable() {
306		return false;
307	}
308
309	/**
310	 * @return bool
311	 */
312	public function isUpdateable() {
313		return false;
314	}
315
316	/**
317	 * @return bool
318	 */
319	public function isDeletable() {
320		return false;
321	}
322
323	/**
324	 * @return bool
325	 */
326	public function isShareable() {
327		return false;
328	}
329
330	/**
331	 * @return Node
332	 * @throws \OCP\Files\NotFoundException
333	 */
334	public function getParent() {
335		throw new NotFoundException();
336	}
337
338	/**
339	 * @return string
340	 */
341	public function getName() {
342		return '';
343	}
344
345	/**
346	 * Returns a view to user's files folder
347	 *
348	 * @param String $userId user ID
349	 * @return \OCP\Files\Folder
350	 * @throws NoUserException
351	 */
352	public function getUserFolder($userId) {
353		$userObject = \OC::$server->getUserManager()->get($userId);
354
355		if ($userObject === null) {
356			$msg = "Backends provided no user object for $userId";
357			\OC::$server->getLogger()->error($msg, ['app' => __CLASS__]);
358			throw new NoUserException($msg);
359		}
360
361		$userId = $userObject->getUID();
362
363		\OC\Files\Filesystem::initMountPoints($userId);
364		$dir = '/' . $userId;
365		$folder = null;
366
367		try {
368			$folder = $this->get($dir);
369		} catch (NotFoundException $e) {
370			$folder = $this->newFolder($dir);
371		}
372
373		$dir = '/files';
374		try {
375			$folder = $folder->get($dir);
376		} catch (NotFoundException $e) {
377			'@phan-var \OC\Files\Node\Folder $folder';
378			$folder = $folder->newFolder($dir);
379		}
380
381		return $folder;
382	}
383
384	private function resolveVirtualNode($fullPath) {
385		$pieces = \explode('/', $fullPath);
386		if ($pieces[1] !== 'meta') {
387			return null;
388		}
389		\array_shift($pieces);
390		\array_shift($pieces);
391		$userSession = \OC::$server->getUserSession();
392		$node = new MetaRootNode($this, $userSession);
393		if (empty($pieces)) {
394			return $node;
395		}
396		return $node->get(\implode('/', $pieces));
397	}
398}
399