objecttree.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <?php
  2. /**
  3. * @author Björn Schießle <schiessle@owncloud.com>
  4. * @author Morris Jobke <hey@morrisjobke.de>
  5. * @author Robin Appelman <icewind@owncloud.com>
  6. * @author Thomas Müller <thomas.mueller@tmit.eu>
  7. * @author Vincent Petry <pvince81@owncloud.com>
  8. *
  9. * @copyright Copyright (c) 2015, ownCloud, Inc.
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OC\Connector\Sabre;
  26. use OC\Connector\Sabre\Exception\InvalidPath;
  27. use OC\Connector\Sabre\Exception\FileLocked;
  28. use OC\Files\FileInfo;
  29. use OC\Files\Mount\MoveableMount;
  30. use OCP\Files\StorageInvalidException;
  31. use OCP\Files\StorageNotAvailableException;
  32. use OCP\Lock\LockedException;
  33. class ObjectTree extends \Sabre\DAV\Tree {
  34. /**
  35. * @var \OC\Files\View
  36. */
  37. protected $fileView;
  38. /**
  39. * @var \OCP\Files\Mount\IMountManager
  40. */
  41. protected $mountManager;
  42. /**
  43. * Creates the object
  44. */
  45. public function __construct() {
  46. }
  47. /**
  48. * @param \Sabre\DAV\INode $rootNode
  49. * @param \OC\Files\View $view
  50. * @param \OCP\Files\Mount\IMountManager $mountManager
  51. */
  52. public function init(\Sabre\DAV\INode $rootNode, \OC\Files\View $view, \OCP\Files\Mount\IMountManager $mountManager) {
  53. $this->rootNode = $rootNode;
  54. $this->fileView = $view;
  55. $this->mountManager = $mountManager;
  56. }
  57. /**
  58. * If the given path is a chunked file name, converts it
  59. * to the real file name. Only applies if the OC-CHUNKED header
  60. * is present.
  61. *
  62. * @param string $path chunk file path to convert
  63. *
  64. * @return string path to real file
  65. */
  66. private function resolveChunkFile($path) {
  67. if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
  68. // resolve to real file name to find the proper node
  69. list($dir, $name) = \Sabre\HTTP\URLUtil::splitPath($path);
  70. if ($dir == '/' || $dir == '.') {
  71. $dir = '';
  72. }
  73. $info = \OC_FileChunking::decodeName($name);
  74. // only replace path if it was really the chunked file
  75. if (isset($info['transferid'])) {
  76. // getNodePath is called for multiple nodes within a chunk
  77. // upload call
  78. $path = $dir . '/' . $info['name'];
  79. $path = ltrim($path, '/');
  80. }
  81. }
  82. return $path;
  83. }
  84. /**
  85. * Returns the INode object for the requested path
  86. *
  87. * @param string $path
  88. * @throws \Sabre\DAV\Exception\ServiceUnavailable
  89. * @throws \Sabre\DAV\Exception\NotFound
  90. * @return \Sabre\DAV\INode
  91. */
  92. public function getNodeForPath($path) {
  93. if (!$this->fileView) {
  94. throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
  95. }
  96. $path = trim($path, '/');
  97. if ($path) {
  98. try {
  99. $this->fileView->verifyPath($path, basename($path));
  100. } catch (\OCP\Files\InvalidPathException $ex) {
  101. throw new InvalidPath($ex->getMessage());
  102. }
  103. }
  104. if (isset($this->cache[$path])) {
  105. return $this->cache[$path];
  106. }
  107. // Is it the root node?
  108. if (!strlen($path)) {
  109. return $this->rootNode;
  110. }
  111. if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
  112. // read from storage
  113. $absPath = $this->fileView->getAbsolutePath($path);
  114. $mount = $this->fileView->getMount($path);
  115. $storage = $mount->getStorage();
  116. $internalPath = $mount->getInternalPath($absPath);
  117. if ($storage) {
  118. /**
  119. * @var \OC\Files\Storage\Storage $storage
  120. */
  121. $scanner = $storage->getScanner($internalPath);
  122. // get data directly
  123. $data = $scanner->getData($internalPath);
  124. $info = new FileInfo($absPath, $storage, $internalPath, $data, $mount);
  125. } else {
  126. $info = null;
  127. }
  128. } else {
  129. // resolve chunk file name to real name, if applicable
  130. $path = $this->resolveChunkFile($path);
  131. // read from cache
  132. try {
  133. $info = $this->fileView->getFileInfo($path);
  134. } catch (StorageNotAvailableException $e) {
  135. throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage not available');
  136. } catch (StorageInvalidException $e) {
  137. throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
  138. } catch (LockedException $e) {
  139. throw new \Sabre\DAV\Exception\Locked();
  140. }
  141. }
  142. if (!$info) {
  143. throw new \Sabre\DAV\Exception\NotFound('File with name ' . $path . ' could not be located');
  144. }
  145. if ($info->getType() === 'dir') {
  146. $node = new \OC\Connector\Sabre\Directory($this->fileView, $info);
  147. } else {
  148. $node = new \OC\Connector\Sabre\File($this->fileView, $info);
  149. }
  150. $this->cache[$path] = $node;
  151. return $node;
  152. }
  153. /**
  154. * Moves a file from one location to another
  155. *
  156. * @param string $sourcePath The path to the file which should be moved
  157. * @param string $destinationPath The full destination path, so not just the destination parent node
  158. * @throws \Sabre\DAV\Exception\BadRequest
  159. * @throws \Sabre\DAV\Exception\ServiceUnavailable
  160. * @throws \Sabre\DAV\Exception\Forbidden
  161. * @return int
  162. */
  163. public function move($sourcePath, $destinationPath) {
  164. if (!$this->fileView) {
  165. throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
  166. }
  167. $targetNodeExists = $this->nodeExists($destinationPath);
  168. $sourceNode = $this->getNodeForPath($sourcePath);
  169. if ($sourceNode instanceof \Sabre\DAV\ICollection && $targetNodeExists) {
  170. throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode . ', target exists');
  171. }
  172. list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourcePath);
  173. list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destinationPath);
  174. $isMovableMount = false;
  175. $sourceMount = $this->mountManager->find($this->fileView->getAbsolutePath($sourcePath));
  176. $internalPath = $sourceMount->getInternalPath($this->fileView->getAbsolutePath($sourcePath));
  177. if ($sourceMount instanceof MoveableMount && $internalPath === '') {
  178. $isMovableMount = true;
  179. }
  180. try {
  181. $sameFolder = ($sourceDir === $destinationDir);
  182. // if we're overwriting or same folder
  183. if ($targetNodeExists || $sameFolder) {
  184. // note that renaming a share mount point is always allowed
  185. if (!$this->fileView->isUpdatable($destinationDir) && !$isMovableMount) {
  186. throw new \Sabre\DAV\Exception\Forbidden();
  187. }
  188. } else {
  189. if (!$this->fileView->isCreatable($destinationDir)) {
  190. throw new \Sabre\DAV\Exception\Forbidden();
  191. }
  192. }
  193. if (!$sameFolder) {
  194. // moving to a different folder, source will be gone, like a deletion
  195. // note that moving a share mount point is always allowed
  196. if (!$this->fileView->isDeletable($sourcePath) && !$isMovableMount) {
  197. throw new \Sabre\DAV\Exception\Forbidden();
  198. }
  199. }
  200. $fileName = basename($destinationPath);
  201. try {
  202. $this->fileView->verifyPath($destinationDir, $fileName);
  203. } catch (\OCP\Files\InvalidPathException $ex) {
  204. throw new InvalidPath($ex->getMessage());
  205. }
  206. $renameOkay = $this->fileView->rename($sourcePath, $destinationPath);
  207. if (!$renameOkay) {
  208. throw new \Sabre\DAV\Exception\Forbidden('');
  209. }
  210. } catch (StorageNotAvailableException $e) {
  211. throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
  212. } catch (LockedException $e) {
  213. throw new FileLocked($e->getMessage(), $e->getCode(), $e);
  214. }
  215. $this->markDirty($sourceDir);
  216. $this->markDirty($destinationDir);
  217. }
  218. /**
  219. * Copies a file or directory.
  220. *
  221. * This method must work recursively and delete the destination
  222. * if it exists
  223. *
  224. * @param string $source
  225. * @param string $destination
  226. * @throws \Sabre\DAV\Exception\ServiceUnavailable
  227. * @return void
  228. */
  229. public function copy($source, $destination) {
  230. if (!$this->fileView) {
  231. throw new \Sabre\DAV\Exception\ServiceUnavailable('filesystem not setup');
  232. }
  233. // this will trigger existence check
  234. $this->getNodeForPath($source);
  235. list($destinationDir, $destinationName) = \Sabre\HTTP\URLUtil::splitPath($destination);
  236. try {
  237. $this->fileView->verifyPath($destinationDir, $destinationName);
  238. } catch (\OCP\Files\InvalidPathException $ex) {
  239. throw new InvalidPath($ex->getMessage());
  240. }
  241. try {
  242. $this->fileView->copy($source, $destination);
  243. } catch (StorageNotAvailableException $e) {
  244. throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
  245. } catch (LockedException $e) {
  246. throw new FileLocked($e->getMessage(), $e->getCode(), $e);
  247. }
  248. list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
  249. $this->markDirty($destinationDir);
  250. }
  251. }