You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

935 lines
24 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bart Visscher <bartv@thisnet.nl>
  7. * @author Christopher Schäpers <kondou@ts.unde.re>
  8. * @author Florin Peter <github@florin-peter.de>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  11. * @author Lukas Reschke <lukas@statuscode.ch>
  12. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Robin Appelman <robin@icewind.nl>
  15. * @author Robin McCorkell <robin@mccorkell.me.uk>
  16. * @author Roeland Jago Douma <roeland@famdouma.nl>
  17. * @author Sam Tuke <mail@samtuke.com>
  18. * @author Stephan Peijnik <speijnik@anexia-it.com>
  19. * @author Vincent Petry <pvince81@owncloud.com>
  20. *
  21. * @license AGPL-3.0
  22. *
  23. * This code is free software: you can redistribute it and/or modify
  24. * it under the terms of the GNU Affero General Public License, version 3,
  25. * as published by the Free Software Foundation.
  26. *
  27. * This program is distributed in the hope that it will be useful,
  28. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  29. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  30. * GNU Affero General Public License for more details.
  31. *
  32. * You should have received a copy of the GNU Affero General Public License, version 3,
  33. * along with this program. If not, see <http://www.gnu.org/licenses/>
  34. *
  35. */
  36. /**
  37. * Class for abstraction of filesystem functions
  38. * This class won't call any filesystem functions for itself but will pass them to the correct OC_Filestorage object
  39. * this class should also handle all the file permission related stuff
  40. *
  41. * Hooks provided:
  42. * read(path)
  43. * write(path, &run)
  44. * post_write(path)
  45. * create(path, &run) (when a file is created, both create and write will be emitted in that order)
  46. * post_create(path)
  47. * delete(path, &run)
  48. * post_delete(path)
  49. * rename(oldpath,newpath, &run)
  50. * post_rename(oldpath,newpath)
  51. * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order)
  52. * post_rename(oldpath,newpath)
  53. * post_initMountPoints(user, user_dir)
  54. *
  55. * the &run parameter can be set to false to prevent the operation from occurring
  56. */
  57. namespace OC\Files;
  58. use OC\Cache\CappedMemoryCache;
  59. use OC\Files\Config\MountProviderCollection;
  60. use OC\Files\Mount\MountPoint;
  61. use OC\Files\Storage\StorageFactory;
  62. use OC\Lockdown\Filesystem\NullStorage;
  63. use OCP\Files\Config\IMountProvider;
  64. use OCP\Files\Mount\IMountPoint;
  65. use OCP\Files\NotFoundException;
  66. use OCP\IUserManager;
  67. class Filesystem {
  68. /**
  69. * @var Mount\Manager $mounts
  70. */
  71. private static $mounts;
  72. public static $loaded = false;
  73. /**
  74. * @var \OC\Files\View $defaultInstance
  75. */
  76. static private $defaultInstance;
  77. static private $usersSetup = array();
  78. static private $normalizedPathCache = null;
  79. static private $listeningForProviders = false;
  80. /**
  81. * classname which used for hooks handling
  82. * used as signalclass in OC_Hooks::emit()
  83. */
  84. const CLASSNAME = 'OC_Filesystem';
  85. /**
  86. * signalname emitted before file renaming
  87. *
  88. * @param string $oldpath
  89. * @param string $newpath
  90. */
  91. const signal_rename = 'rename';
  92. /**
  93. * signal emitted after file renaming
  94. *
  95. * @param string $oldpath
  96. * @param string $newpath
  97. */
  98. const signal_post_rename = 'post_rename';
  99. /**
  100. * signal emitted before file/dir creation
  101. *
  102. * @param string $path
  103. * @param bool $run changing this flag to false in hook handler will cancel event
  104. */
  105. const signal_create = 'create';
  106. /**
  107. * signal emitted after file/dir creation
  108. *
  109. * @param string $path
  110. * @param bool $run changing this flag to false in hook handler will cancel event
  111. */
  112. const signal_post_create = 'post_create';
  113. /**
  114. * signal emits before file/dir copy
  115. *
  116. * @param string $oldpath
  117. * @param string $newpath
  118. * @param bool $run changing this flag to false in hook handler will cancel event
  119. */
  120. const signal_copy = 'copy';
  121. /**
  122. * signal emits after file/dir copy
  123. *
  124. * @param string $oldpath
  125. * @param string $newpath
  126. */
  127. const signal_post_copy = 'post_copy';
  128. /**
  129. * signal emits before file/dir save
  130. *
  131. * @param string $path
  132. * @param bool $run changing this flag to false in hook handler will cancel event
  133. */
  134. const signal_write = 'write';
  135. /**
  136. * signal emits after file/dir save
  137. *
  138. * @param string $path
  139. */
  140. const signal_post_write = 'post_write';
  141. /**
  142. * signal emitted before file/dir update
  143. *
  144. * @param string $path
  145. * @param bool $run changing this flag to false in hook handler will cancel event
  146. */
  147. const signal_update = 'update';
  148. /**
  149. * signal emitted after file/dir update
  150. *
  151. * @param string $path
  152. * @param bool $run changing this flag to false in hook handler will cancel event
  153. */
  154. const signal_post_update = 'post_update';
  155. /**
  156. * signal emits when reading file/dir
  157. *
  158. * @param string $path
  159. */
  160. const signal_read = 'read';
  161. /**
  162. * signal emits when removing file/dir
  163. *
  164. * @param string $path
  165. */
  166. const signal_delete = 'delete';
  167. /**
  168. * parameters definitions for signals
  169. */
  170. const signal_param_path = 'path';
  171. const signal_param_oldpath = 'oldpath';
  172. const signal_param_newpath = 'newpath';
  173. /**
  174. * run - changing this flag to false in hook handler will cancel event
  175. */
  176. const signal_param_run = 'run';
  177. const signal_create_mount = 'create_mount';
  178. const signal_delete_mount = 'delete_mount';
  179. const signal_param_mount_type = 'mounttype';
  180. const signal_param_users = 'users';
  181. /**
  182. * @var \OC\Files\Storage\StorageFactory $loader
  183. */
  184. private static $loader;
  185. /** @var bool */
  186. private static $logWarningWhenAddingStorageWrapper = true;
  187. /**
  188. * @param bool $shouldLog
  189. * @return bool previous value
  190. * @internal
  191. */
  192. public static function logWarningWhenAddingStorageWrapper($shouldLog) {
  193. $previousValue = self::$logWarningWhenAddingStorageWrapper;
  194. self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
  195. return $previousValue;
  196. }
  197. /**
  198. * @param string $wrapperName
  199. * @param callable $wrapper
  200. * @param int $priority
  201. */
  202. public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) {
  203. if (self::$logWarningWhenAddingStorageWrapper) {
  204. \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [
  205. 'wrapper' => $wrapperName,
  206. 'app' => 'filesystem',
  207. ]);
  208. }
  209. $mounts = self::getMountManager()->getAll();
  210. if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) {
  211. // do not re-wrap if storage with this name already existed
  212. return;
  213. }
  214. }
  215. /**
  216. * Returns the storage factory
  217. *
  218. * @return \OCP\Files\Storage\IStorageFactory
  219. */
  220. public static function getLoader() {
  221. if (!self::$loader) {
  222. self::$loader = new StorageFactory();
  223. }
  224. return self::$loader;
  225. }
  226. /**
  227. * Returns the mount manager
  228. *
  229. * @return \OC\Files\Mount\Manager
  230. */
  231. public static function getMountManager($user = '') {
  232. if (!self::$mounts) {
  233. \OC_Util::setupFS($user);
  234. }
  235. return self::$mounts;
  236. }
  237. /**
  238. * get the mountpoint of the storage object for a path
  239. * ( note: because a storage is not always mounted inside the fakeroot, the
  240. * returned mountpoint is relative to the absolute root of the filesystem
  241. * and doesn't take the chroot into account )
  242. *
  243. * @param string $path
  244. * @return string
  245. */
  246. static public function getMountPoint($path) {
  247. if (!self::$mounts) {
  248. \OC_Util::setupFS();
  249. }
  250. $mount = self::$mounts->find($path);
  251. if ($mount) {
  252. return $mount->getMountPoint();
  253. } else {
  254. return '';
  255. }
  256. }
  257. /**
  258. * get a list of all mount points in a directory
  259. *
  260. * @param string $path
  261. * @return string[]
  262. */
  263. static public function getMountPoints($path) {
  264. if (!self::$mounts) {
  265. \OC_Util::setupFS();
  266. }
  267. $result = array();
  268. $mounts = self::$mounts->findIn($path);
  269. foreach ($mounts as $mount) {
  270. $result[] = $mount->getMountPoint();
  271. }
  272. return $result;
  273. }
  274. /**
  275. * get the storage mounted at $mountPoint
  276. *
  277. * @param string $mountPoint
  278. * @return \OC\Files\Storage\Storage
  279. */
  280. public static function getStorage($mountPoint) {
  281. if (!self::$mounts) {
  282. \OC_Util::setupFS();
  283. }
  284. $mount = self::$mounts->find($mountPoint);
  285. return $mount->getStorage();
  286. }
  287. /**
  288. * @param string $id
  289. * @return Mount\MountPoint[]
  290. */
  291. public static function getMountByStorageId($id) {
  292. if (!self::$mounts) {
  293. \OC_Util::setupFS();
  294. }
  295. return self::$mounts->findByStorageId($id);
  296. }
  297. /**
  298. * @param int $id
  299. * @return Mount\MountPoint[]
  300. */
  301. public static function getMountByNumericId($id) {
  302. if (!self::$mounts) {
  303. \OC_Util::setupFS();
  304. }
  305. return self::$mounts->findByNumericId($id);
  306. }
  307. /**
  308. * resolve a path to a storage and internal path
  309. *
  310. * @param string $path
  311. * @return array an array consisting of the storage and the internal path
  312. */
  313. static public function resolvePath($path) {
  314. if (!self::$mounts) {
  315. \OC_Util::setupFS();
  316. }
  317. $mount = self::$mounts->find($path);
  318. if ($mount) {
  319. return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/'));
  320. } else {
  321. return array(null, null);
  322. }
  323. }
  324. static public function init($user, $root) {
  325. if (self::$defaultInstance) {
  326. return false;
  327. }
  328. self::getLoader();
  329. self::$defaultInstance = new View($root);
  330. if (!self::$mounts) {
  331. self::$mounts = \OC::$server->getMountManager();
  332. }
  333. //load custom mount config
  334. self::initMountPoints($user);
  335. self::$loaded = true;
  336. return true;
  337. }
  338. static public function initMountManager() {
  339. if (!self::$mounts) {
  340. self::$mounts = \OC::$server->getMountManager();
  341. }
  342. }
  343. /**
  344. * Initialize system and personal mount points for a user
  345. *
  346. * @param string $user
  347. * @throws \OC\User\NoUserException if the user is not available
  348. */
  349. public static function initMountPoints($user = '') {
  350. if ($user == '') {
  351. $user = \OC_User::getUser();
  352. }
  353. if ($user === null || $user === false || $user === '') {
  354. throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session');
  355. }
  356. if (isset(self::$usersSetup[$user])) {
  357. return;
  358. }
  359. self::$usersSetup[$user] = true;
  360. $userManager = \OC::$server->getUserManager();
  361. $userObject = $userManager->get($user);
  362. if (is_null($userObject)) {
  363. \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR);
  364. // reset flag, this will make it possible to rethrow the exception if called again
  365. unset(self::$usersSetup[$user]);
  366. throw new \OC\User\NoUserException('Backends provided no user object for ' . $user);
  367. }
  368. $realUid = $userObject->getUID();
  369. // workaround in case of different casings
  370. if ($user !== $realUid) {
  371. $stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50));
  372. \OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN);
  373. $user = $realUid;
  374. // again with the correct casing
  375. if (isset(self::$usersSetup[$user])) {
  376. return;
  377. }
  378. self::$usersSetup[$user] = true;
  379. }
  380. if (\OC::$server->getLockdownManager()->canAccessFilesystem()) {
  381. /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
  382. $mountConfigManager = \OC::$server->getMountProviderCollection();
  383. // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
  384. $homeMount = $mountConfigManager->getHomeMountForUser($userObject);
  385. self::getMountManager()->addMount($homeMount);
  386. \OC\Files\Filesystem::getStorage($user);
  387. // Chance to mount for other storages
  388. if ($userObject) {
  389. $mounts = $mountConfigManager->getMountsForUser($userObject);
  390. array_walk($mounts, array(self::$mounts, 'addMount'));
  391. $mounts[] = $homeMount;
  392. $mountConfigManager->registerMounts($userObject, $mounts);
  393. }
  394. self::listenForNewMountProviders($mountConfigManager, $userManager);
  395. } else {
  396. self::getMountManager()->addMount(new MountPoint(
  397. new NullStorage([]),
  398. '/' . $user
  399. ));
  400. self::getMountManager()->addMount(new MountPoint(
  401. new NullStorage([]),
  402. '/' . $user . '/files'
  403. ));
  404. }
  405. \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user));
  406. }
  407. /**
  408. * Get mounts from mount providers that are registered after setup
  409. *
  410. * @param MountProviderCollection $mountConfigManager
  411. * @param IUserManager $userManager
  412. */
  413. private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) {
  414. if (!self::$listeningForProviders) {
  415. self::$listeningForProviders = true;
  416. $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) {
  417. foreach (Filesystem::$usersSetup as $user => $setup) {
  418. $userObject = $userManager->get($user);
  419. if ($userObject) {
  420. $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader());
  421. array_walk($mounts, array(self::$mounts, 'addMount'));
  422. }
  423. }
  424. });
  425. }
  426. }
  427. /**
  428. * get the default filesystem view
  429. *
  430. * @return View
  431. */
  432. static public function getView() {
  433. return self::$defaultInstance;
  434. }
  435. /**
  436. * tear down the filesystem, removing all storage providers
  437. */
  438. static public function tearDown() {
  439. self::clearMounts();
  440. self::$defaultInstance = null;
  441. }
  442. /**
  443. * get the relative path of the root data directory for the current user
  444. *
  445. * @return string
  446. *
  447. * Returns path like /admin/files
  448. */
  449. static public function getRoot() {
  450. if (!self::$defaultInstance) {
  451. return null;
  452. }
  453. return self::$defaultInstance->getRoot();
  454. }
  455. /**
  456. * clear all mounts and storage backends
  457. */
  458. public static function clearMounts() {
  459. if (self::$mounts) {
  460. self::$usersSetup = array();
  461. self::$mounts->clear();
  462. }
  463. }
  464. /**
  465. * mount an \OC\Files\Storage\Storage in our virtual filesystem
  466. *
  467. * @param \OC\Files\Storage\Storage|string $class
  468. * @param array $arguments
  469. * @param string $mountpoint
  470. */
  471. static public function mount($class, $arguments, $mountpoint) {
  472. if (!self::$mounts) {
  473. \OC_Util::setupFS();
  474. }
  475. $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader());
  476. self::$mounts->addMount($mount);
  477. }
  478. /**
  479. * return the path to a local version of the file
  480. * we need this because we can't know if a file is stored local or not from
  481. * outside the filestorage and for some purposes a local file is needed
  482. *
  483. * @param string $path
  484. * @return string
  485. */
  486. static public function getLocalFile($path) {
  487. return self::$defaultInstance->getLocalFile($path);
  488. }
  489. /**
  490. * @param string $path
  491. * @return string
  492. */
  493. static public function getLocalFolder($path) {
  494. return self::$defaultInstance->getLocalFolder($path);
  495. }
  496. /**
  497. * return path to file which reflects one visible in browser
  498. *
  499. * @param string $path
  500. * @return string
  501. */
  502. static public function getLocalPath($path) {
  503. $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files';
  504. $newpath = $path;
  505. if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
  506. $newpath = substr($path, strlen($datadir));
  507. }
  508. return $newpath;
  509. }
  510. /**
  511. * check if the requested path is valid
  512. *
  513. * @param string $path
  514. * @return bool
  515. */
  516. static public function isValidPath($path) {
  517. $path = self::normalizePath($path);
  518. if (!$path || $path[0] !== '/') {
  519. $path = '/' . $path;
  520. }
  521. if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') {
  522. return false;
  523. }
  524. return true;
  525. }
  526. /**
  527. * checks if a file is blacklisted for storage in the filesystem
  528. * Listens to write and rename hooks
  529. *
  530. * @param array $data from hook
  531. */
  532. static public function isBlacklisted($data) {
  533. if (isset($data['path'])) {
  534. $path = $data['path'];
  535. } else if (isset($data['newpath'])) {
  536. $path = $data['newpath'];
  537. }
  538. if (isset($path)) {
  539. if (self::isFileBlacklisted($path)) {
  540. $data['run'] = false;
  541. }
  542. }
  543. }
  544. /**
  545. * @param string $filename
  546. * @return bool
  547. */
  548. static public function isFileBlacklisted($filename) {
  549. $filename = self::normalizePath($filename);
  550. $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess'));
  551. $filename = strtolower(basename($filename));
  552. return in_array($filename, $blacklist);
  553. }
  554. /**
  555. * check if the directory should be ignored when scanning
  556. * NOTE: the special directories . and .. would cause never ending recursion
  557. *
  558. * @param String $dir
  559. * @return boolean
  560. */
  561. static public function isIgnoredDir($dir) {
  562. if ($dir === '.' || $dir === '..') {
  563. return true;
  564. }
  565. return false;
  566. }
  567. /**
  568. * following functions are equivalent to their php builtin equivalents for arguments/return values.
  569. */
  570. static public function mkdir($path) {
  571. return self::$defaultInstance->mkdir($path);
  572. }
  573. static public function rmdir($path) {
  574. return self::$defaultInstance->rmdir($path);
  575. }
  576. static public function opendir($path) {
  577. return self::$defaultInstance->opendir($path);
  578. }
  579. static public function readdir($path) {
  580. return self::$defaultInstance->readdir($path);
  581. }
  582. static public function is_dir($path) {
  583. return self::$defaultInstance->is_dir($path);
  584. }
  585. static public function is_file($path) {
  586. return self::$defaultInstance->is_file($path);
  587. }
  588. static public function stat($path) {
  589. return self::$defaultInstance->stat($path);
  590. }
  591. static public function filetype($path) {
  592. return self::$defaultInstance->filetype($path);
  593. }
  594. static public function filesize($path) {
  595. return self::$defaultInstance->filesize($path);
  596. }
  597. static public function readfile($path) {
  598. return self::$defaultInstance->readfile($path);
  599. }
  600. static public function isCreatable($path) {
  601. return self::$defaultInstance->isCreatable($path);
  602. }
  603. static public function isReadable($path) {
  604. return self::$defaultInstance->isReadable($path);
  605. }
  606. static public function isUpdatable($path) {
  607. return self::$defaultInstance->isUpdatable($path);
  608. }
  609. static public function isDeletable($path) {
  610. return self::$defaultInstance->isDeletable($path);
  611. }
  612. static public function isSharable($path) {
  613. return self::$defaultInstance->isSharable($path);
  614. }
  615. static public function file_exists($path) {
  616. return self::$defaultInstance->file_exists($path);
  617. }
  618. static public function filemtime($path) {
  619. return self::$defaultInstance->filemtime($path);
  620. }
  621. static public function touch($path, $mtime = null) {
  622. return self::$defaultInstance->touch($path, $mtime);
  623. }
  624. /**
  625. * @return string
  626. */
  627. static public function file_get_contents($path) {
  628. return self::$defaultInstance->file_get_contents($path);
  629. }
  630. static public function file_put_contents($path, $data) {
  631. return self::$defaultInstance->file_put_contents($path, $data);
  632. }
  633. static public function unlink($path) {
  634. return self::$defaultInstance->unlink($path);
  635. }
  636. static public function rename($path1, $path2) {
  637. return self::$defaultInstance->rename($path1, $path2);
  638. }
  639. static public function copy($path1, $path2) {
  640. return self::$defaultInstance->copy($path1, $path2);
  641. }
  642. static public function fopen($path, $mode) {
  643. return self::$defaultInstance->fopen($path, $mode);
  644. }
  645. /**
  646. * @return string
  647. */
  648. static public function toTmpFile($path) {
  649. return self::$defaultInstance->toTmpFile($path);
  650. }
  651. static public function fromTmpFile($tmpFile, $path) {
  652. return self::$defaultInstance->fromTmpFile($tmpFile, $path);
  653. }
  654. static public function getMimeType($path) {
  655. return self::$defaultInstance->getMimeType($path);
  656. }
  657. static public function hash($type, $path, $raw = false) {
  658. return self::$defaultInstance->hash($type, $path, $raw);
  659. }
  660. static public function free_space($path = '/') {
  661. return self::$defaultInstance->free_space($path);
  662. }
  663. static public function search($query) {
  664. return self::$defaultInstance->search($query);
  665. }
  666. /**
  667. * @param string $query
  668. */
  669. static public function searchByMime($query) {
  670. return self::$defaultInstance->searchByMime($query);
  671. }
  672. /**
  673. * @param string|int $tag name or tag id
  674. * @param string $userId owner of the tags
  675. * @return FileInfo[] array or file info
  676. */
  677. static public function searchByTag($tag, $userId) {
  678. return self::$defaultInstance->searchByTag($tag, $userId);
  679. }
  680. /**
  681. * check if a file or folder has been updated since $time
  682. *
  683. * @param string $path
  684. * @param int $time
  685. * @return bool
  686. */
  687. static public function hasUpdated($path, $time) {
  688. return self::$defaultInstance->hasUpdated($path, $time);
  689. }
  690. /**
  691. * Fix common problems with a file path
  692. *
  693. * @param string $path
  694. * @param bool $stripTrailingSlash whether to strip the trailing slash
  695. * @param bool $isAbsolutePath whether the given path is absolute
  696. * @param bool $keepUnicode true to disable unicode normalization
  697. * @return string
  698. */
  699. public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) {
  700. if (is_null(self::$normalizedPathCache)) {
  701. self::$normalizedPathCache = new CappedMemoryCache();
  702. }
  703. /**
  704. * FIXME: This is a workaround for existing classes and files which call
  705. * this function with another type than a valid string. This
  706. * conversion should get removed as soon as all existing
  707. * function calls have been fixed.
  708. */
  709. $path = (string)$path;
  710. $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]);
  711. if (isset(self::$normalizedPathCache[$cacheKey])) {
  712. return self::$normalizedPathCache[$cacheKey];
  713. }
  714. if ($path == '') {
  715. return '/';
  716. }
  717. //normalize unicode if possible
  718. if (!$keepUnicode) {
  719. $path = \OC_Util::normalizeUnicode($path);
  720. }
  721. //no windows style slashes
  722. $path = str_replace('\\', '/', $path);
  723. //add leading slash
  724. if ($path[0] !== '/') {
  725. $path = '/' . $path;
  726. }
  727. // remove '/./'
  728. // ugly, but str_replace() can't replace them all in one go
  729. // as the replacement itself is part of the search string
  730. // which will only be found during the next iteration
  731. while (strpos($path, '/./') !== false) {
  732. $path = str_replace('/./', '/', $path);
  733. }
  734. // remove sequences of slashes
  735. $path = preg_replace('#/{2,}#', '/', $path);
  736. //remove trailing slash
  737. if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') {
  738. $path = substr($path, 0, -1);
  739. }
  740. // remove trailing '/.'
  741. if (substr($path, -2) == '/.') {
  742. $path = substr($path, 0, -2);
  743. }
  744. $normalizedPath = $path;
  745. self::$normalizedPathCache[$cacheKey] = $normalizedPath;
  746. return $normalizedPath;
  747. }
  748. /**
  749. * get the filesystem info
  750. *
  751. * @param string $path
  752. * @param boolean $includeMountPoints whether to add mountpoint sizes,
  753. * defaults to true
  754. * @return \OC\Files\FileInfo|bool False if file does not exist
  755. */
  756. public static function getFileInfo($path, $includeMountPoints = true) {
  757. return self::$defaultInstance->getFileInfo($path, $includeMountPoints);
  758. }
  759. /**
  760. * change file metadata
  761. *
  762. * @param string $path
  763. * @param array $data
  764. * @return int
  765. *
  766. * returns the fileid of the updated file
  767. */
  768. public static function putFileInfo($path, $data) {
  769. return self::$defaultInstance->putFileInfo($path, $data);
  770. }
  771. /**
  772. * get the content of a directory
  773. *
  774. * @param string $directory path under datadirectory
  775. * @param string $mimetype_filter limit returned content to this mimetype or mimepart
  776. * @return \OC\Files\FileInfo[]
  777. */
  778. public static function getDirectoryContent($directory, $mimetype_filter = '') {
  779. return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter);
  780. }
  781. /**
  782. * Get the path of a file by id
  783. *
  784. * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
  785. *
  786. * @param int $id
  787. * @throws NotFoundException
  788. * @return string
  789. */
  790. public static function getPath($id) {
  791. return self::$defaultInstance->getPath($id);
  792. }
  793. /**
  794. * Get the owner for a file or folder
  795. *
  796. * @param string $path
  797. * @return string
  798. */
  799. public static function getOwner($path) {
  800. return self::$defaultInstance->getOwner($path);
  801. }
  802. /**
  803. * get the ETag for a file or folder
  804. *
  805. * @param string $path
  806. * @return string
  807. */
  808. static public function getETag($path) {
  809. return self::$defaultInstance->getETag($path);
  810. }
  811. }