ObjectTree.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bjoern Schiessle <bjoern@schiessle.org>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Lukas Reschke <lukas@statuscode.ch>
  8. * @author Morris Jobke <hey@morrisjobke.de>
  9. * @author Robin Appelman <robin@icewind.nl>
  10. * @author Roeland Jago Douma <roeland@famdouma.nl>
  11. * @author Thomas Müller <thomas.mueller@tmit.eu>
  12. * @author Vincent Petry <vincent@nextcloud.com>
  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. namespace OCA\DAV\Connector\Sabre;
  30. use OC\Files\FileInfo;
  31. use OC\Files\Storage\FailedStorage;
  32. use OCA\DAV\Connector\Sabre\Exception\FileLocked;
  33. use OCA\DAV\Connector\Sabre\Exception\Forbidden;
  34. use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
  35. use OCP\Files\ForbiddenException;
  36. use OCP\Files\StorageInvalidException;
  37. use OCP\Files\StorageNotAvailableException;
  38. use OCP\Lock\LockedException;
  39. class ObjectTree extends CachingTree {
  40. /**
  41. * @var \OC\Files\View
  42. */
  43. protected $fileView;
  44. /**
  45. * @var \OCP\Files\Mount\IMountManager
  46. */
  47. protected $mountManager;
  48. /**
  49. * Creates the object
  50. */
  51. public function __construct() {
  52. }
  53. /**
  54. * @param \Sabre\DAV\INode $rootNode
  55. * @param \OC\Files\View $view
  56. * @param \OCP\Files\Mount\IMountManager $mountManager
  57. */
  58. public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
  59. $this->rootNode = $rootNode;
  60. $this->fileView = $view;
  61. $this->mountManager = $mountManager;
  62. }
  63. /**
  64. * If the given path is a chunked file name, converts it
  65. * to the real file name. Only applies if the OC-CHUNKED header
  66. * is present.
  67. *
  68. * @param string $path chunk file path to convert
  69. *
  70. * @return string path to real file
  71. */
  72. private function resolveChunkFile($path) {
  73. if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
  74. // resolve to real file name to find the proper node
  75. [$dir, $name] = \Sabre\Uri\split($path);
  76. if ($dir === '/' || $dir === '.') {
  77. $dir = '';
  78. }
  79. $info = \OC_FileChunking::decodeName($name);
  80. // only replace path if it was really the chunked file
  81. if (isset($info['transferid'])) {
  82. // getNodePath is called for multiple nodes within a chunk
  83. // upload call
  84. $path = $dir . '/' . $info['name'];
  85. $path = ltrim($path, '/');
  86. }
  87. }
  88. return $path;
  89. }
  90. /**
  91. * Returns the INode object for the requested path
  92. *
  93. * @param string $path
  94. * @return \Sabre\DAV\INode
  95. * @throws InvalidPath
  96. * @throws \Sabre\DAV\Exception\Locked
  97. * @throws \Sabre\DAV\Exception\NotFound
  98. * @throws \Sabre\DAV\Exception\ServiceUnavailable
  99. */
  100. public function getNodeForPath($path) {
  101. if (!$this->fileView) {
  102. throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
  103. }
  104. $path = trim($path, '/');
  105. if (isset($this->cache[$path])) {
  106. return $this->cache[$path];
  107. }
  108. if ($path) {
  109. try {
  110. $this->fileView->verifyPath($path, basename($path));
  111. } catch (\OCP\Files\InvalidPathException $ex) {
  112. throw new InvalidPath($ex->getMessage());
  113. }
  114. }
  115. // Is it the root node?
  116. if (!strlen($path)) {
  117. return $this->rootNode;
  118. }
  119. if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
  120. // read from storage
  121. $absPath = $this->fileView->getAbsolutePath($path);
  122. $mount = $this->fileView->getMount($path);
  123. $storage = $mount->getStorage();
  124. $internalPath = $mount->getInternalPath($absPath);
  125. if ($storage && $storage->file_exists($internalPath)) {
  126. /**
  127. * @var \OC\Files\Storage\Storage $storage
  128. */
  129. // get data directly
  130. $data = $storage->getMetaData($internalPath);
  131. $info = new FileInfo($absPath, $storage, $internalPath, $data, $mount);
  132. } else {
  133. $info = null;
  134. }
  135. } else {
  136. // resolve chunk file name to real name, if applicable
  137. $path = $this->resolveChunkFile($path);
  138. // read from cache
  139. try {
  140. $info = $this->fileView->getFileInfo($path);
  141. if ($info instanceof \OCP\Files\FileInfo && $info->getStorage()->instanceOfStorage(FailedStorage::class)) {
  142. throw new StorageNotAvailableException();
  143. }
  144. } catch (StorageNotAvailableException $e) {
  145. throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage is temporarily not available', 0, $e);
  146. } catch (StorageInvalidException $e) {
  147. throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
  148. } catch (LockedException $e) {
  149. throw new \Sabre\DAV\Exception\Locked();
  150. } catch (ForbiddenException $e) {
  151. throw new \Sabre\DAV\Exception\Forbidden();
  152. }
  153. }
  154. if (!$info) {
  155. throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
  156. }
  157. if ($info->getType() === 'dir') {
  158. $node = new \OCA\DAV\Connector\Sabre\Directory($this->fileView, $info, $this);
  159. } else {
  160. $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
  161. }
  162. $this->cache[$path] = $node;
  163. return $node;
  164. }
  165. /**
  166. * Copies a file or directory.
  167. *
  168. * This method must work recursively and delete the destination
  169. * if it exists
  170. *
  171. * @param string $sourcePath
  172. * @param string $destinationPath
  173. * @throws FileLocked
  174. * @throws Forbidden
  175. * @throws InvalidPath
  176. * @throws \Exception
  177. * @throws \Sabre\DAV\Exception\Forbidden
  178. * @throws \Sabre\DAV\Exception\Locked
  179. * @throws \Sabre\DAV\Exception\NotFound
  180. * @throws \Sabre\DAV\Exception\ServiceUnavailable
  181. * @return void
  182. */
  183. public function copy($sourcePath, $destinationPath) {
  184. if (!$this->fileView) {
  185. throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
  186. }
  187. $info = $this->fileView->getFileInfo(dirname($destinationPath));
  188. if ($this->fileView->file_exists($destinationPath)) {
  189. $destinationPermission = $info && $info->isUpdateable();
  190. } else {
  191. $destinationPermission = $info && $info->isCreatable();
  192. }
  193. if (!$destinationPermission) {
  194. throw new Forbidden('No permissions to copy object.');
  195. }
  196. // this will trigger existence check
  197. $this->getNodeForPath($sourcePath);
  198. [$destinationDir, $destinationName] = \Sabre\Uri\split($destinationPath);
  199. try {
  200. $this->fileView->verifyPath($destinationDir, $destinationName);
  201. } catch (\OCP\Files\InvalidPathException $ex) {
  202. throw new InvalidPath($ex->getMessage());
  203. }
  204. try {
  205. $this->fileView->copy($sourcePath, $destinationPath);
  206. } catch (StorageNotAvailableException $e) {
  207. throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
  208. } catch (ForbiddenException $ex) {
  209. throw new Forbidden($ex->getMessage(), $ex->getRetry());
  210. } catch (LockedException $e) {
  211. throw new FileLocked($e->getMessage(), $e->getCode(), $e);
  212. }
  213. [$destinationDir,] = \Sabre\Uri\split($destinationPath);
  214. $this->markDirty($destinationDir);
  215. }
  216. }