sharedstorage.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. <?php
  2. /**
  3. * @author Bart Visscher <bartv@thisnet.nl>
  4. * @author Björn Schießle <schiessle@owncloud.com>
  5. * @author Joas Schilling <nickvergessen@owncloud.com>
  6. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Robin Appelman <icewind@owncloud.com>
  9. * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
  10. * @author scambra <sergio@entrecables.com>
  11. * @author Vincent Petry <pvince81@owncloud.com>
  12. *
  13. * @copyright Copyright (c) 2015, ownCloud, Inc.
  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 OC\Files\Storage;
  30. use OC\Files\Filesystem;
  31. use OCA\Files_Sharing\ISharedStorage;
  32. use OCA\Files_Sharing\SharedMount;
  33. /**
  34. * Convert target path to source path and pass the function call to the correct storage provider
  35. */
  36. class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
  37. private $share; // the shared resource
  38. private $files = array();
  39. private static $isInitialized = array();
  40. public function __construct($arguments) {
  41. $this->share = $arguments['share'];
  42. }
  43. /**
  44. * get id of the mount point
  45. * @return string
  46. */
  47. public function getId() {
  48. return 'shared::' . $this->getMountPoint();
  49. }
  50. /**
  51. * get file cache of the shared item source
  52. * @return int
  53. */
  54. public function getSourceId() {
  55. return (int) $this->share['file_source'];
  56. }
  57. /**
  58. * Get the source file path, permissions, and owner for a shared file
  59. * @param string $target Shared target file path
  60. * @return Returns array with the keys path, permissions, and owner or false if not found
  61. */
  62. public function getFile($target) {
  63. if (!isset($this->files[$target])) {
  64. // Check for partial files
  65. if (pathinfo($target, PATHINFO_EXTENSION) === 'part') {
  66. $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType());
  67. if ($source) {
  68. $source['path'] .= '.part';
  69. // All partial files have delete permission
  70. $source['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
  71. }
  72. } else {
  73. $source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType());
  74. }
  75. $this->files[$target] = $source;
  76. }
  77. return $this->files[$target];
  78. }
  79. /**
  80. * Get the source file path for a shared file
  81. * @param string $target Shared target file path
  82. * @return string|false source file path or false if not found
  83. */
  84. public function getSourcePath($target) {
  85. $source = $this->getFile($target);
  86. if ($source) {
  87. if (!isset($source['fullPath'])) {
  88. \OC\Files\Filesystem::initMountPoints($source['fileOwner']);
  89. $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
  90. if (is_array($mount) && !empty($mount)) {
  91. $this->files[$target]['fullPath'] = $mount[key($mount)]->getMountPoint() . $source['path'];
  92. } else {
  93. $this->files[$target]['fullPath'] = false;
  94. \OCP\Util::writeLog('files_sharing', "Unable to get mount for shared storage '" . $source['storage'] . "' user '" . $source['fileOwner'] . "'", \OCP\Util::ERROR);
  95. }
  96. }
  97. return $this->files[$target]['fullPath'];
  98. }
  99. return false;
  100. }
  101. /**
  102. * Get the permissions granted for a shared file
  103. * @param string $target Shared target file path
  104. * @return int CRUDS permissions granted
  105. */
  106. public function getPermissions($target = '') {
  107. $permissions = $this->share['permissions'];
  108. // part files and the mount point always have delete permissions
  109. if ($target === '' || pathinfo($target, PATHINFO_EXTENSION) === 'part') {
  110. $permissions |= \OCP\Constants::PERMISSION_DELETE;
  111. }
  112. if (\OCP\Util::isSharingDisabledForUser()) {
  113. $permissions &= ~\OCP\Constants::PERMISSION_SHARE;
  114. }
  115. return $permissions;
  116. }
  117. public function mkdir($path) {
  118. if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) {
  119. return false;
  120. } else if ($source = $this->getSourcePath($path)) {
  121. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  122. return $storage->mkdir($internalPath);
  123. }
  124. return false;
  125. }
  126. /**
  127. * Delete the directory if DELETE permission is granted
  128. * @param string $path
  129. * @return boolean
  130. */
  131. public function rmdir($path) {
  132. // never delete a share mount point
  133. if(empty($path)) {
  134. return false;
  135. }
  136. if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) {
  137. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  138. return $storage->rmdir($internalPath);
  139. }
  140. return false;
  141. }
  142. public function opendir($path) {
  143. $source = $this->getSourcePath($path);
  144. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  145. return $storage->opendir($internalPath);
  146. }
  147. public function is_dir($path) {
  148. $source = $this->getSourcePath($path);
  149. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  150. return $storage->is_dir($internalPath);
  151. }
  152. public function is_file($path) {
  153. if ($source = $this->getSourcePath($path)) {
  154. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  155. return $storage->is_file($internalPath);
  156. }
  157. return false;
  158. }
  159. public function stat($path) {
  160. if ($path == '' || $path == '/') {
  161. $stat['size'] = $this->filesize($path);
  162. $stat['mtime'] = $this->filemtime($path);
  163. return $stat;
  164. } else if ($source = $this->getSourcePath($path)) {
  165. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  166. return $storage->stat($internalPath);
  167. }
  168. return false;
  169. }
  170. public function filetype($path) {
  171. if ($path == '' || $path == '/') {
  172. return 'dir';
  173. } else if ($source = $this->getSourcePath($path)) {
  174. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  175. return $storage->filetype($internalPath);
  176. }
  177. return false;
  178. }
  179. public function filesize($path) {
  180. $source = $this->getSourcePath($path);
  181. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  182. return $storage->filesize($internalPath);
  183. }
  184. public function isCreatable($path) {
  185. return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_CREATE);
  186. }
  187. public function isReadable($path) {
  188. return $this->file_exists($path);
  189. }
  190. public function isUpdatable($path) {
  191. return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_UPDATE);
  192. }
  193. public function isDeletable($path) {
  194. return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_DELETE);
  195. }
  196. public function isSharable($path) {
  197. if (\OCP\Util::isSharingDisabledForUser()) {
  198. return false;
  199. }
  200. return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE);
  201. }
  202. public function file_exists($path) {
  203. if ($path == '' || $path == '/') {
  204. return true;
  205. } else if ($source = $this->getSourcePath($path)) {
  206. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  207. return $storage->file_exists($internalPath);
  208. }
  209. return false;
  210. }
  211. public function filemtime($path) {
  212. $source = $this->getSourcePath($path);
  213. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  214. return $storage->filemtime($internalPath);
  215. }
  216. public function file_get_contents($path) {
  217. $source = $this->getSourcePath($path);
  218. if ($source) {
  219. $info = array(
  220. 'target' => $this->getMountPoint() . $path,
  221. 'source' => $source,
  222. );
  223. \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
  224. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  225. return $storage->file_get_contents($internalPath);
  226. }
  227. }
  228. public function file_put_contents($path, $data) {
  229. if ($source = $this->getSourcePath($path)) {
  230. // Check if permission is granted
  231. if (($this->file_exists($path) && !$this->isUpdatable($path))
  232. || ($this->is_dir($path) && !$this->isCreatable($path))
  233. ) {
  234. return false;
  235. }
  236. $info = array(
  237. 'target' => $this->getMountPoint() . '/' . $path,
  238. 'source' => $source,
  239. );
  240. \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
  241. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  242. $result = $storage->file_put_contents($internalPath, $data);
  243. return $result;
  244. }
  245. return false;
  246. }
  247. /**
  248. * Delete the file if DELETE permission is granted
  249. * @param string $path
  250. * @return boolean
  251. */
  252. public function unlink($path) {
  253. // never delete a share mount point
  254. if (empty($path)) {
  255. return false;
  256. }
  257. if ($source = $this->getSourcePath($path)) {
  258. if ($this->isDeletable($path)) {
  259. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  260. return $storage->unlink($internalPath);
  261. }
  262. }
  263. return false;
  264. }
  265. public function rename($path1, $path2) {
  266. // we need the paths relative to data/user/files
  267. $relPath1 = $this->getMountPoint() . '/' . $path1;
  268. $relPath2 = $this->getMountPoint() . '/' . $path2;
  269. $pathinfo = pathinfo($relPath1);
  270. $isPartFile = (isset($pathinfo['extension']) && $pathinfo['extension'] === 'part');
  271. $targetExists = $this->file_exists($path2);
  272. $sameFolder = (dirname($relPath1) === dirname($relPath2));
  273. if ($targetExists || ($sameFolder && !$isPartFile)) {
  274. // note that renaming a share mount point is always allowed
  275. if (!$this->isUpdatable('')) {
  276. return false;
  277. }
  278. } else {
  279. if (!$this->isCreatable('')) {
  280. return false;
  281. }
  282. }
  283. // for part files we need to ask for the owner and path from the parent directory because
  284. // the file cache doesn't return any results for part files
  285. if ($isPartFile) {
  286. list($user1, $path1) = \OCA\Files_Sharing\Helper::getUidAndFilename($pathinfo['dirname']);
  287. $path1 = $path1 . '/' . $pathinfo['basename'];
  288. } else {
  289. list($user1, $path1) = \OCA\Files_Sharing\Helper::getUidAndFilename($relPath1);
  290. }
  291. $targetFilename = basename($relPath2);
  292. list($user2, $path2) = \OCA\Files_Sharing\Helper::getUidAndFilename(dirname($relPath2));
  293. $rootView = new \OC\Files\View('');
  294. return $rootView->rename('/' . $user1 . '/files/' . $path1, '/' . $user2 . '/files/' . $path2 . '/' . $targetFilename);
  295. }
  296. public function copy($path1, $path2) {
  297. // Copy the file if CREATE permission is granted
  298. if ($this->isCreatable(dirname($path2))) {
  299. $oldSource = $this->getSourcePath($path1);
  300. $newSource = $this->getSourcePath(dirname($path2)) . '/' . basename($path2);
  301. $rootView = new \OC\Files\View('');
  302. return $rootView->copy($oldSource, $newSource);
  303. }
  304. return false;
  305. }
  306. public function fopen($path, $mode) {
  307. if ($source = $this->getSourcePath($path)) {
  308. switch ($mode) {
  309. case 'r+':
  310. case 'rb+':
  311. case 'w+':
  312. case 'wb+':
  313. case 'x+':
  314. case 'xb+':
  315. case 'a+':
  316. case 'ab+':
  317. case 'w':
  318. case 'wb':
  319. case 'x':
  320. case 'xb':
  321. case 'a':
  322. case 'ab':
  323. $creatable = $this->isCreatable($path);
  324. $updatable = $this->isUpdatable($path);
  325. // if neither permissions given, no need to continue
  326. if (!$creatable && !$updatable) {
  327. return false;
  328. }
  329. $exists = $this->file_exists($path);
  330. // if a file exists, updatable permissions are required
  331. if ($exists && !$updatable) {
  332. return false;
  333. }
  334. // part file is allowed if !$creatable but the final file is $updatable
  335. if (pathinfo($path, PATHINFO_EXTENSION) !== 'part') {
  336. if (!$exists && !$creatable) {
  337. return false;
  338. }
  339. }
  340. }
  341. $info = array(
  342. 'target' => $this->getMountPoint() . $path,
  343. 'source' => $source,
  344. 'mode' => $mode,
  345. );
  346. \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info);
  347. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  348. return $storage->fopen($internalPath, $mode);
  349. }
  350. return false;
  351. }
  352. public function getMimeType($path) {
  353. if ($source = $this->getSourcePath($path)) {
  354. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  355. return $storage->getMimeType($internalPath);
  356. }
  357. return false;
  358. }
  359. public function free_space($path) {
  360. $source = $this->getSourcePath($path);
  361. if ($source) {
  362. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  363. return $storage->free_space($internalPath);
  364. }
  365. return \OCP\Files\FileInfo::SPACE_UNKNOWN;
  366. }
  367. public function getLocalFile($path) {
  368. if ($source = $this->getSourcePath($path)) {
  369. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  370. return $storage->getLocalFile($internalPath);
  371. }
  372. return false;
  373. }
  374. public function touch($path, $mtime = null) {
  375. if ($source = $this->getSourcePath($path)) {
  376. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  377. return $storage->touch($internalPath, $mtime);
  378. }
  379. return false;
  380. }
  381. public static function setup($options) {
  382. $user = $options['user'];
  383. $shares = \OCP\Share::getItemsSharedWithUser('file', $user);
  384. $manager = Filesystem::getMountManager();
  385. $loader = Filesystem::getLoader();
  386. if (
  387. !isset(self::$isInitialized[$user]) && (
  388. !\OCP\User::isLoggedIn()
  389. || \OCP\User::getUser() != $options['user']
  390. || $shares
  391. )
  392. ) {
  393. foreach ($shares as $share) {
  394. // don't mount shares where we have no permissions
  395. if ($share['permissions'] > 0) {
  396. $mount = new SharedMount(
  397. '\OC\Files\Storage\Shared',
  398. $options['user_dir'] . '/' . $share['file_target'],
  399. array(
  400. 'share' => $share,
  401. 'user' => $user
  402. ),
  403. $loader
  404. );
  405. $manager->addMount($mount);
  406. }
  407. }
  408. }
  409. self::$isInitialized[$user] = true;
  410. }
  411. /**
  412. * return mount point of share, relative to data/user/files
  413. *
  414. * @return string
  415. */
  416. public function getMountPoint() {
  417. return $this->share['file_target'];
  418. }
  419. public function setMountPoint($path) {
  420. $this->share['file_target'] = $path;
  421. }
  422. public function getShareType() {
  423. return $this->share['share_type'];
  424. }
  425. /**
  426. * does the group share already has a user specific unique name
  427. * @return bool
  428. */
  429. public function uniqueNameSet() {
  430. return (isset($this->share['unique_name']) && $this->share['unique_name']);
  431. }
  432. /**
  433. * the share now uses a unique name of this user
  434. *
  435. * @brief the share now uses a unique name of this user
  436. */
  437. public function setUniqueName() {
  438. $this->share['unique_name'] = true;
  439. }
  440. /**
  441. * get share ID
  442. * @return integer unique share ID
  443. */
  444. public function getShareId() {
  445. return $this->share['id'];
  446. }
  447. /**
  448. * get the user who shared the file
  449. * @return string
  450. */
  451. public function getSharedFrom() {
  452. return $this->share['uid_owner'];
  453. }
  454. /**
  455. * @return array
  456. */
  457. public function getShare() {
  458. return $this->share;
  459. }
  460. /**
  461. * return share type, can be "file" or "folder"
  462. * @return string
  463. */
  464. public function getItemType() {
  465. return $this->share['item_type'];
  466. }
  467. public function hasUpdated($path, $time) {
  468. return $this->filemtime($path) > $time;
  469. }
  470. public function getCache($path = '', $storage = null) {
  471. if (!$storage) {
  472. $storage = $this;
  473. }
  474. return new \OC\Files\Cache\Shared_Cache($storage);
  475. }
  476. public function getScanner($path = '', $storage = null) {
  477. if (!$storage) {
  478. $storage = $this;
  479. }
  480. return new \OC\Files\Cache\SharedScanner($storage);
  481. }
  482. public function getWatcher($path = '', $storage = null) {
  483. if (!$storage) {
  484. $storage = $this;
  485. }
  486. return new \OC\Files\Cache\Shared_Watcher($storage);
  487. }
  488. public function getOwner($path) {
  489. if ($path == '') {
  490. $path = $this->getMountPoint();
  491. }
  492. $source = $this->getFile($path);
  493. if ($source) {
  494. return $source['fileOwner'];
  495. }
  496. return false;
  497. }
  498. public function getETag($path) {
  499. if ($path == '') {
  500. $path = $this->getMountPoint();
  501. }
  502. if ($source = $this->getSourcePath($path)) {
  503. list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
  504. return $storage->getETag($internalPath);
  505. }
  506. return null;
  507. }
  508. /**
  509. * unshare complete storage, also the grouped shares
  510. *
  511. * @return bool
  512. */
  513. public function unshareStorage() {
  514. $result = true;
  515. if (!empty($this->share['grouped'])) {
  516. foreach ($this->share['grouped'] as $share) {
  517. $result = $result && \OCP\Share::unshareFromSelf($share['item_type'], $share['file_target']);
  518. }
  519. }
  520. $result = $result && \OCP\Share::unshareFromSelf($this->getItemType(), $this->getMountPoint());
  521. return $result;
  522. }
  523. }