PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/public/share.php

https://github.com/sezuan/core
PHP | 1761 lines | 1238 code | 73 blank | 450 comment | 442 complexity | eb4d0094590a2114c5ca73b49cba8182 MD5 | raw file
Possible License(s): AGPL-3.0, AGPL-1.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Michael Gapczynski
  6. * @copyright 2012 Michael Gapczynski mtgap@owncloud.com
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. namespace OCP;
  22. /**
  23. * This class provides the ability for apps to share their content between users.
  24. * Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
  25. *
  26. * It provides the following hooks:
  27. * - post_shared
  28. */
  29. class Share {
  30. const SHARE_TYPE_USER = 0;
  31. const SHARE_TYPE_GROUP = 1;
  32. const SHARE_TYPE_LINK = 3;
  33. const SHARE_TYPE_EMAIL = 4;
  34. const SHARE_TYPE_CONTACT = 5;
  35. const SHARE_TYPE_REMOTE = 6;
  36. /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
  37. * Construct permissions for share() and setPermissions with Or (|) e.g.
  38. * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
  39. *
  40. * Check if permission is granted with And (&) e.g. Check if delete is
  41. * granted: if ($permissions & PERMISSION_DELETE)
  42. *
  43. * Remove permissions with And (&) and Not (~) e.g. Remove the update
  44. * permission: $permissions &= ~PERMISSION_UPDATE
  45. *
  46. * Apps are required to handle permissions on their own, this class only
  47. * stores and manages the permissions of shares
  48. * @see lib/public/constants.php
  49. */
  50. const FORMAT_NONE = -1;
  51. const FORMAT_STATUSES = -2;
  52. const FORMAT_SOURCES = -3;
  53. const TOKEN_LENGTH = 32; // see db_structure.xml
  54. private static $shareTypeUserAndGroups = -1;
  55. private static $shareTypeGroupUserUnique = 2;
  56. private static $backends = array();
  57. private static $backendTypes = array();
  58. private static $isResharingAllowed;
  59. /**
  60. * @brief Register a sharing backend class that implements OCP\Share_Backend for an item type
  61. * @param string Item type
  62. * @param string Backend class
  63. * @param string (optional) Depends on item type
  64. * @param array (optional) List of supported file extensions if this item type depends on files
  65. * @return Returns true if backend is registered or false if error
  66. */
  67. public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
  68. if (self::isEnabled()) {
  69. if (!isset(self::$backendTypes[$itemType])) {
  70. self::$backendTypes[$itemType] = array(
  71. 'class' => $class,
  72. 'collectionOf' => $collectionOf,
  73. 'supportedFileExtensions' => $supportedFileExtensions
  74. );
  75. if(count(self::$backendTypes) === 1) {
  76. \OC_Util::addScript('core', 'share');
  77. \OC_Util::addStyle('core', 'share');
  78. }
  79. return true;
  80. }
  81. \OC_Log::write('OCP\Share',
  82. 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class']
  83. .' is already registered for '.$itemType,
  84. \OC_Log::WARN);
  85. }
  86. return false;
  87. }
  88. /**
  89. * @brief Check if the Share API is enabled
  90. * @return Returns true if enabled or false
  91. *
  92. * The Share API is enabled by default if not configured
  93. *
  94. */
  95. public static function isEnabled() {
  96. if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
  97. return true;
  98. }
  99. return false;
  100. }
  101. /**
  102. * @brief Prepare a path to be passed to DB as file_target
  103. * @return string Prepared path
  104. */
  105. public static function prepFileTarget( $path ) {
  106. // Paths in DB are stored with leading slashes, so add one if necessary
  107. if ( substr( $path, 0, 1 ) !== '/' ) {
  108. $path = '/' . $path;
  109. }
  110. return $path;
  111. }
  112. /**
  113. * @brief Find which users can access a shared item
  114. * @param $path to the file
  115. * @param $user owner of the file
  116. * @param include owner to the list of users with access to the file
  117. * @return array
  118. * @note $path needs to be relative to user data dir, e.g. 'file.txt'
  119. * not '/admin/data/file.txt'
  120. */
  121. public static function getUsersSharingFile($path, $user, $includeOwner = false) {
  122. $shares = array();
  123. $publicShare = false;
  124. $source = -1;
  125. $cache = false;
  126. $view = new \OC\Files\View('/' . $user . '/files/');
  127. $meta = $view->getFileInfo(\OC\Files\Filesystem::normalizePath($path));
  128. if($meta !== false) {
  129. $source = $meta['fileid'];
  130. $cache = new \OC\Files\Cache\Cache($meta['storage']);
  131. }
  132. while ($source !== -1) {
  133. // Fetch all shares of this file path from DB
  134. $query = \OC_DB::prepare(
  135. 'SELECT `share_with`
  136. FROM
  137. `*PREFIX*share`
  138. WHERE
  139. `item_source` = ? AND `share_type` = ?'
  140. );
  141. $result = $query->execute(array($source, self::SHARE_TYPE_USER));
  142. if (\OCP\DB::isError($result)) {
  143. \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
  144. } else {
  145. while ($row = $result->fetchRow()) {
  146. $shares[] = $row['share_with'];
  147. }
  148. }
  149. // We also need to take group shares into account
  150. $query = \OC_DB::prepare(
  151. 'SELECT `share_with`
  152. FROM
  153. `*PREFIX*share`
  154. WHERE
  155. `item_source` = ? AND `share_type` = ?'
  156. );
  157. $result = $query->execute(array($source, self::SHARE_TYPE_GROUP));
  158. if (\OCP\DB::isError($result)) {
  159. \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
  160. } else {
  161. while ($row = $result->fetchRow()) {
  162. $usersInGroup = \OC_Group::usersInGroup($row['share_with']);
  163. $shares = array_merge($shares, $usersInGroup);
  164. }
  165. }
  166. //check for public link shares
  167. if (!$publicShare) {
  168. $query = \OC_DB::prepare(
  169. 'SELECT `share_with`
  170. FROM
  171. `*PREFIX*share`
  172. WHERE
  173. `item_source` = ? AND `share_type` = ?'
  174. );
  175. $result = $query->execute(array($source, self::SHARE_TYPE_LINK));
  176. if (\OCP\DB::isError($result)) {
  177. \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
  178. } else {
  179. if ($result->fetchRow()) {
  180. $publicShare = true;
  181. }
  182. }
  183. }
  184. // let's get the parent for the next round
  185. $meta = $cache->get((int)$source);
  186. if($meta !== false) {
  187. $source = (int)$meta['parent'];
  188. } else {
  189. $source = -1;
  190. }
  191. }
  192. // Include owner in list of users, if requested
  193. if ($includeOwner) {
  194. $shares[] = $user;
  195. }
  196. return array("users" => array_unique($shares), "public" => $publicShare);
  197. }
  198. /**
  199. * @brief Get the items of item type shared with the current user
  200. * @param string Item type
  201. * @param int Format (optional) Format type must be defined by the backend
  202. * @param int Number of items to return (optional) Returns all by default
  203. * @return Return depends on format
  204. */
  205. public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE,
  206. $parameters = null, $limit = -1, $includeCollections = false) {
  207. return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
  208. $parameters, $limit, $includeCollections);
  209. }
  210. /**
  211. * @brief Get the item of item type shared with the current user
  212. * @param string Item type
  213. * @param string Item target
  214. * @param int Format (optional) Format type must be defined by the backend
  215. * @return Return depends on format
  216. */
  217. public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE,
  218. $parameters = null, $includeCollections = false) {
  219. return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
  220. $parameters, 1, $includeCollections);
  221. }
  222. /**
  223. * @brief Get the item of item type shared with the current user by source
  224. * @param string Item type
  225. * @param string Item source
  226. * @param int Format (optional) Format type must be defined by the backend
  227. * @return Return depends on format
  228. */
  229. public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE,
  230. $parameters = null, $includeCollections = false) {
  231. return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format,
  232. $parameters, 1, $includeCollections, true);
  233. }
  234. /**
  235. * @brief Get the item of item type shared by a link
  236. * @param string Item type
  237. * @param string Item source
  238. * @param string Owner of link
  239. * @return Item
  240. */
  241. public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
  242. return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE,
  243. null, 1);
  244. }
  245. /**
  246. * @brief Get the item shared by a token
  247. * @param string token
  248. * @return Item
  249. */
  250. public static function getShareByToken($token) {
  251. $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?', 1);
  252. $result = $query->execute(array($token));
  253. if (\OC_DB::isError($result)) {
  254. \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR);
  255. }
  256. return $result->fetchRow();
  257. }
  258. /**
  259. * @brief resolves reshares down to the last real share
  260. * @param $linkItem
  261. * @return $fileOwner
  262. */
  263. public static function resolveReShare($linkItem)
  264. {
  265. if (isset($linkItem['parent'])) {
  266. $parent = $linkItem['parent'];
  267. while (isset($parent)) {
  268. $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `id` = ?', 1);
  269. $item = $query->execute(array($parent))->fetchRow();
  270. if (isset($item['parent'])) {
  271. $parent = $item['parent'];
  272. } else {
  273. return $item;
  274. }
  275. }
  276. }
  277. return $linkItem;
  278. }
  279. /**
  280. * @brief Get the shared items of item type owned by the current user
  281. * @param string Item type
  282. * @param int Format (optional) Format type must be defined by the backend
  283. * @param int Number of items to return (optional) Returns all by default
  284. * @return Return depends on format
  285. */
  286. public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null,
  287. $limit = -1, $includeCollections = false) {
  288. return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format,
  289. $parameters, $limit, $includeCollections);
  290. }
  291. /**
  292. * @brief Get the shared item of item type owned by the current user
  293. * @param string Item type
  294. * @param string Item source
  295. * @param int Format (optional) Format type must be defined by the backend
  296. * @return Return depends on format
  297. */
  298. public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE,
  299. $parameters = null, $includeCollections = false) {
  300. return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format,
  301. $parameters, -1, $includeCollections);
  302. }
  303. /**
  304. * Get all users an item is shared with
  305. * @param string Item type
  306. * @param string Item source
  307. * @param string Owner
  308. * @param bool Include collections
  309. * @return Return array of users
  310. */
  311. public static function getUsersItemShared($itemType, $itemSource, $uidOwner, $includeCollections = false) {
  312. $users = array();
  313. $items = self::getItems($itemType, $itemSource, null, null, $uidOwner, self::FORMAT_NONE, null, -1, $includeCollections);
  314. if ($items) {
  315. foreach ($items as $item) {
  316. if ((int)$item['share_type'] === self::SHARE_TYPE_USER) {
  317. $users[] = $item['share_with'];
  318. } else if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
  319. $users = array_merge($users, \OC_Group::usersInGroup($item['share_with']));
  320. }
  321. }
  322. }
  323. return $users;
  324. }
  325. /**
  326. * @brief Share an item with a user, group, or via private link
  327. * @param string Item type
  328. * @param string Item source
  329. * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
  330. * @param string User or group the item is being shared with
  331. * @param int CRUDS permissions
  332. * @return bool|string Returns true on success or false on failure, Returns token on success for links
  333. */
  334. public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions) {
  335. $uidOwner = \OC_User::getUser();
  336. $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global');
  337. // Verify share type and sharing conditions are met
  338. if ($shareType === self::SHARE_TYPE_USER) {
  339. if ($shareWith == $uidOwner) {
  340. $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is the item owner';
  341. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  342. throw new \Exception($message);
  343. }
  344. if (!\OC_User::userExists($shareWith)) {
  345. $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' does not exist';
  346. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  347. throw new \Exception($message);
  348. }
  349. if ($sharingPolicy == 'groups_only') {
  350. $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith));
  351. if (empty($inGroup)) {
  352. $message = 'Sharing '.$itemSource.' failed, because the user '
  353. .$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of';
  354. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  355. throw new \Exception($message);
  356. }
  357. }
  358. // Check if the item source is already shared with the user, either from the same owner or a different user
  359. if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups,
  360. $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) {
  361. // Only allow the same share to occur again if it is the same
  362. // owner and is not a user share, this use case is for increasing
  363. // permissions for a specific user
  364. if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
  365. $message = 'Sharing '.$itemSource.' failed, because this item is already shared with '.$shareWith;
  366. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  367. throw new \Exception($message);
  368. }
  369. }
  370. } else if ($shareType === self::SHARE_TYPE_GROUP) {
  371. if (!\OC_Group::groupExists($shareWith)) {
  372. $message = 'Sharing '.$itemSource.' failed, because the group '.$shareWith.' does not exist';
  373. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  374. throw new \Exception($message);
  375. }
  376. if ($sharingPolicy == 'groups_only' && !\OC_Group::inGroup($uidOwner, $shareWith)) {
  377. $message = 'Sharing '.$itemSource.' failed, because '
  378. .$uidOwner.' is not a member of the group '.$shareWith;
  379. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  380. throw new \Exception($message);
  381. }
  382. // Check if the item source is already shared with the group, either from the same owner or a different user
  383. // The check for each user in the group is done inside the put() function
  384. if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith,
  385. null, self::FORMAT_NONE, null, 1, true, true)) {
  386. // Only allow the same share to occur again if it is the same
  387. // owner and is not a group share, this use case is for increasing
  388. // permissions for a specific user
  389. if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) {
  390. $message = 'Sharing '.$itemSource.' failed, because this item is already shared with '.$shareWith;
  391. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  392. throw new \Exception($message);
  393. }
  394. }
  395. // Convert share with into an array with the keys group and users
  396. $group = $shareWith;
  397. $shareWith = array();
  398. $shareWith['group'] = $group;
  399. $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner));
  400. } else if ($shareType === self::SHARE_TYPE_LINK) {
  401. if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
  402. // when updating a link share
  403. if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null,
  404. $uidOwner, self::FORMAT_NONE, null, 1)) {
  405. // remember old token
  406. $oldToken = $checkExists['token'];
  407. //delete the old share
  408. self::delete($checkExists['id']);
  409. }
  410. // Generate hash of password - same method as user passwords
  411. if (isset($shareWith)) {
  412. $forcePortable = (CRYPT_BLOWFISH != 1);
  413. $hasher = new \PasswordHash(8, $forcePortable);
  414. $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', ''));
  415. }
  416. // Generate token
  417. if (isset($oldToken)) {
  418. $token = $oldToken;
  419. } else {
  420. $token = \OC_Util::generate_random_bytes(self::TOKEN_LENGTH);
  421. }
  422. $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions,
  423. null, $token);
  424. if ($result) {
  425. return $token;
  426. } else {
  427. return false;
  428. }
  429. }
  430. $message = 'Sharing '.$itemSource.' failed, because sharing with links is not allowed';
  431. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  432. throw new \Exception($message);
  433. return false;
  434. // } else if ($shareType === self::SHARE_TYPE_CONTACT) {
  435. // if (!\OC_App::isEnabled('contacts')) {
  436. // $message = 'Sharing '.$itemSource.' failed, because the contacts app is not enabled';
  437. // \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  438. // return false;
  439. // }
  440. // $vcard = \OC_Contacts_App::getContactVCard($shareWith);
  441. // if (!isset($vcard)) {
  442. // $message = 'Sharing '.$itemSource.' failed, because the contact does not exist';
  443. // \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  444. // throw new \Exception($message);
  445. // }
  446. // $details = \OC_Contacts_VCard::structureContact($vcard);
  447. // // TODO Add ownCloud user to contacts vcard
  448. // if (!isset($details['EMAIL'])) {
  449. // $message = 'Sharing '.$itemSource.' failed, because no email address is associated with the contact';
  450. // \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  451. // throw new \Exception($message);
  452. // }
  453. // return self::shareItem($itemType, $itemSource, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions);
  454. } else {
  455. // Future share types need to include their own conditions
  456. $message = 'Share type '.$shareType.' is not valid for '.$itemSource;
  457. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  458. throw new \Exception($message);
  459. }
  460. // If the item is a folder, scan through the folder looking for equivalent item types
  461. // if ($itemType == 'folder') {
  462. // $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true);
  463. // if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) {
  464. // for ($i = 0; $i < count($files); $i++) {
  465. // $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource));
  466. // if ($files[$i]['mimetype'] == 'httpd/unix-directory'
  467. // && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/')
  468. // ) {
  469. // // Continue scanning into child folders
  470. // array_push($files, $children);
  471. // } else {
  472. // // Check file extension for an equivalent item type to convert to
  473. // $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1));
  474. // foreach (self::$backends as $type => $backend) {
  475. // if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) {
  476. // $itemType = $type;
  477. // break;
  478. // }
  479. // }
  480. // // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted
  481. // self::put($itemType, $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder);
  482. // }
  483. // }
  484. // return true;
  485. // }
  486. // return false;
  487. // } else {
  488. // Put the item into the database
  489. return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions);
  490. // }
  491. }
  492. /**
  493. * @brief Unshare an item from a user, group, or delete a private link
  494. * @param string Item type
  495. * @param string Item source
  496. * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
  497. * @param string User or group the item is being shared with
  498. * @return Returns true on success or false on failure
  499. */
  500. public static function unshare($itemType, $itemSource, $shareType, $shareWith) {
  501. if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(),
  502. self::FORMAT_NONE, null, 1)) {
  503. // Pass all the vars we have for now, they may be useful
  504. \OC_Hook::emit('OCP\Share', 'pre_unshare', array(
  505. 'itemType' => $itemType,
  506. 'itemSource' => $itemSource,
  507. 'fileSource' => $item['file_source'],
  508. 'shareType' => $shareType,
  509. 'shareWith' => $shareWith,
  510. 'itemParent' => $item['parent'],
  511. ));
  512. self::delete($item['id']);
  513. \OC_Hook::emit('OCP\Share', 'post_unshare', array(
  514. 'itemType' => $itemType,
  515. 'itemSource' => $itemSource,
  516. 'shareType' => $shareType,
  517. 'shareWith' => $shareWith,
  518. 'itemParent' => $item['parent'],
  519. ));
  520. return true;
  521. }
  522. return false;
  523. }
  524. /**
  525. * @brief Unshare an item from all users, groups, and remove all links
  526. * @param string Item type
  527. * @param string Item source
  528. * @return Returns true on success or false on failure
  529. */
  530. public static function unshareAll($itemType, $itemSource) {
  531. if ($shares = self::getItemShared($itemType, $itemSource)) {
  532. // Pass all the vars we have for now, they may be useful
  533. \OC_Hook::emit('OCP\Share', 'pre_unshareAll', array(
  534. 'itemType' => $itemType,
  535. 'itemSource' => $itemSource,
  536. 'shares' => $shares
  537. ));
  538. foreach ($shares as $share) {
  539. self::delete($share['id']);
  540. }
  541. \OC_Hook::emit('OCP\Share', 'post_unshareAll', array(
  542. 'itemType' => $itemType,
  543. 'itemSource' => $itemSource,
  544. 'shares' => $shares
  545. ));
  546. return true;
  547. }
  548. return false;
  549. }
  550. /**
  551. * @brief Unshare an item shared with the current user
  552. * @param string Item type
  553. * @param string Item target
  554. * @return Returns true on success or false on failure
  555. *
  556. * Unsharing from self is not allowed for items inside collections
  557. *
  558. */
  559. public static function unshareFromSelf($itemType, $itemTarget) {
  560. if ($item = self::getItemSharedWith($itemType, $itemTarget)) {
  561. if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) {
  562. // Insert an extra row for the group share and set permission
  563. // to 0 to prevent it from showing up for the user
  564. $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share`'
  565. .' (`item_type`, `item_source`, `item_target`, `parent`, `share_type`,'
  566. .' `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`, `file_target`)'
  567. .' VALUES (?,?,?,?,?,?,?,?,?,?,?)');
  568. $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'],
  569. $item['id'], self::$shareTypeGroupUserUnique,
  570. \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'],
  571. $item['file_target']));
  572. \OC_DB::insertid('*PREFIX*share');
  573. // Delete all reshares by this user of the group share
  574. self::delete($item['id'], true, \OC_User::getUser());
  575. } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
  576. // Set permission to 0 to prevent it from showing up for the user
  577. $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
  578. $query->execute(array(0, $item['id']));
  579. self::delete($item['id'], true);
  580. } else {
  581. self::delete($item['id']);
  582. }
  583. return true;
  584. }
  585. return false;
  586. }
  587. /**
  588. * @brief Set the permissions of an item for a specific user or group
  589. * @param string Item type
  590. * @param string Item source
  591. * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
  592. * @param string User or group the item is being shared with
  593. * @param int CRUDS permissions
  594. * @return Returns true on success or false on failure
  595. */
  596. public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) {
  597. if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith,
  598. \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) {
  599. // Check if this item is a reshare and verify that the permissions
  600. // granted don't exceed the parent shared item
  601. if (isset($item['parent'])) {
  602. $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*share` WHERE `id` = ?', 1);
  603. $result = $query->execute(array($item['parent']))->fetchRow();
  604. if (~(int)$result['permissions'] & $permissions) {
  605. $message = 'Setting permissions for '.$itemSource.' failed,'
  606. .' because the permissions exceed permissions granted to '.\OC_User::getUser();
  607. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  608. throw new \Exception($message);
  609. }
  610. }
  611. $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
  612. $query->execute(array($permissions, $item['id']));
  613. if ($itemType === 'file' || $itemType === 'folder') {
  614. \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
  615. 'itemType' => $itemType,
  616. 'itemSource' => $itemSource,
  617. 'shareType' => $shareType,
  618. 'shareWith' => $shareWith,
  619. 'uidOwner' => \OC_User::getUser(),
  620. 'permissions' => $permissions,
  621. 'path' => $item['path'],
  622. ));
  623. }
  624. // Check if permissions were removed
  625. if ($item['permissions'] & ~$permissions) {
  626. // If share permission is removed all reshares must be deleted
  627. if (($item['permissions'] & PERMISSION_SHARE) && (~$permissions & PERMISSION_SHARE)) {
  628. self::delete($item['id'], true);
  629. } else {
  630. $ids = array();
  631. $parents = array($item['id']);
  632. while (!empty($parents)) {
  633. $parents = "'".implode("','", $parents)."'";
  634. $query = \OC_DB::prepare('SELECT `id`, `permissions` FROM `*PREFIX*share`'
  635. .' WHERE `parent` IN ('.$parents.')');
  636. $result = $query->execute();
  637. // Reset parents array, only go through loop again if
  638. // items are found that need permissions removed
  639. $parents = array();
  640. while ($item = $result->fetchRow()) {
  641. // Check if permissions need to be removed
  642. if ($item['permissions'] & ~$permissions) {
  643. // Add to list of items that need permissions removed
  644. $ids[] = $item['id'];
  645. $parents[] = $item['id'];
  646. }
  647. }
  648. }
  649. // Remove the permissions for all reshares of this item
  650. if (!empty($ids)) {
  651. $ids = "'".implode("','", $ids)."'";
  652. // TODO this should be done with Doctrine platform objects
  653. if (\OC_Config::getValue( "dbtype") === 'oci') {
  654. $andOp = 'BITAND(`permissions`, ?)';
  655. } else {
  656. $andOp = '`permissions` & ?';
  657. }
  658. $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = '.$andOp
  659. .' WHERE `id` IN ('.$ids.')');
  660. $query->execute(array($permissions));
  661. }
  662. }
  663. }
  664. return true;
  665. }
  666. $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found';
  667. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  668. throw new \Exception($message);
  669. }
  670. public static function setExpirationDate($itemType, $itemSource, $date) {
  671. if ($items = self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(),
  672. self::FORMAT_NONE, null, -1, false)) {
  673. if (!empty($items)) {
  674. if ($date == '') {
  675. $date = null;
  676. } else {
  677. $date = new \DateTime($date);
  678. $date = date('Y-m-d H:i', $date->format('U') - $date->getOffset());
  679. }
  680. $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?');
  681. foreach ($items as $item) {
  682. $query->execute(array($date, $item['id']));
  683. }
  684. return true;
  685. }
  686. }
  687. return false;
  688. }
  689. /**
  690. * @brief Get the backend class for the specified item type
  691. * @param string Item type
  692. * @return Sharing backend object
  693. */
  694. private static function getBackend($itemType) {
  695. if (isset(self::$backends[$itemType])) {
  696. return self::$backends[$itemType];
  697. } else if (isset(self::$backendTypes[$itemType]['class'])) {
  698. $class = self::$backendTypes[$itemType]['class'];
  699. if (class_exists($class)) {
  700. self::$backends[$itemType] = new $class;
  701. if (!(self::$backends[$itemType] instanceof Share_Backend)) {
  702. $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend';
  703. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  704. throw new \Exception($message);
  705. }
  706. return self::$backends[$itemType];
  707. } else {
  708. $message = 'Sharing backend '.$class.' not found';
  709. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  710. throw new \Exception($message);
  711. }
  712. }
  713. $message = 'Sharing backend for '.$itemType.' not found';
  714. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  715. throw new \Exception($message);
  716. }
  717. /**
  718. * @brief Check if resharing is allowed
  719. * @return Returns true if allowed or false
  720. *
  721. * Resharing is allowed by default if not configured
  722. *
  723. */
  724. private static function isResharingAllowed() {
  725. if (!isset(self::$isResharingAllowed)) {
  726. if (\OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
  727. self::$isResharingAllowed = true;
  728. } else {
  729. self::$isResharingAllowed = false;
  730. }
  731. }
  732. return self::$isResharingAllowed;
  733. }
  734. /**
  735. * @brief Get a list of collection item types for the specified item type
  736. * @param string Item type
  737. * @return array
  738. */
  739. private static function getCollectionItemTypes($itemType) {
  740. $collectionTypes = array($itemType);
  741. foreach (self::$backendTypes as $type => $backend) {
  742. if (in_array($backend['collectionOf'], $collectionTypes)) {
  743. $collectionTypes[] = $type;
  744. }
  745. }
  746. // TODO Add option for collections to be collection of themselves, only 'folder' does it now...
  747. if (!self::getBackend($itemType) instanceof Share_Backend_Collection || $itemType != 'folder') {
  748. unset($collectionTypes[0]);
  749. }
  750. // Return array if collections were found or the item type is a
  751. // collection itself - collections can be inside collections
  752. if (count($collectionTypes) > 0) {
  753. return $collectionTypes;
  754. }
  755. return false;
  756. }
  757. /**
  758. * @brief Get shared items from the database
  759. * @param string Item type
  760. * @param string Item source or target (optional)
  761. * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique
  762. * @param string User or group the item is being shared with
  763. * @param string User that is the owner of shared items (optional)
  764. * @param int Format to convert items to with formatItems()
  765. * @param mixed Parameters to pass to formatItems()
  766. * @param int Number of items to return, -1 to return all matches (optional)
  767. * @param bool Include collection item types (optional)
  768. * @return mixed
  769. *
  770. * See public functions getItem(s)... for parameter usage
  771. *
  772. */
  773. private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null,
  774. $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1,
  775. $includeCollections = false, $itemShareWithBySource = false) {
  776. if (!self::isEnabled()) {
  777. if ($limit == 1 || (isset($uidOwner) && isset($item))) {
  778. return false;
  779. } else {
  780. return array();
  781. }
  782. }
  783. $backend = self::getBackend($itemType);
  784. $collectionTypes = false;
  785. // Get filesystem root to add it to the file target and remove from the
  786. // file source, match file_source with the file cache
  787. if ($itemType == 'file' || $itemType == 'folder') {
  788. $root = \OC\Files\Filesystem::getRoot();
  789. $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`';
  790. if (!isset($item)) {
  791. $where .= ' WHERE `file_target` IS NOT NULL';
  792. }
  793. $fileDependent = true;
  794. $queryArgs = array();
  795. } else {
  796. $fileDependent = false;
  797. $root = '';
  798. if ($includeCollections && !isset($item) && ($collectionTypes = self::getCollectionItemTypes($itemType))) {
  799. // If includeCollections is true, find collections of this item type, e.g. a music album contains songs
  800. if (!in_array($itemType, $collectionTypes)) {
  801. $itemTypes = array_merge(array($itemType), $collectionTypes);
  802. } else {
  803. $itemTypes = $collectionTypes;
  804. }
  805. $placeholders = join(',', array_fill(0, count($itemTypes), '?'));
  806. $where = ' WHERE `item_type` IN ('.$placeholders.'))';
  807. $queryArgs = $itemTypes;
  808. } else {
  809. $where = ' WHERE `item_type` = ?';
  810. $queryArgs = array($itemType);
  811. }
  812. }
  813. if (isset($shareType)) {
  814. // Include all user and group items
  815. if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
  816. $where .= ' AND `share_type` IN (?,?,?)';
  817. $queryArgs[] = self::SHARE_TYPE_USER;
  818. $queryArgs[] = self::SHARE_TYPE_GROUP;
  819. $queryArgs[] = self::$shareTypeGroupUserUnique;
  820. $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith));
  821. $placeholders = join(',', array_fill(0, count($userAndGroups), '?'));
  822. $where .= ' AND `share_with` IN ('.$placeholders.')';
  823. $queryArgs = array_merge($queryArgs, $userAndGroups);
  824. // Don't include own group shares
  825. $where .= ' AND `uid_owner` != ?';
  826. $queryArgs[] = $shareWith;
  827. } else {
  828. $where .= ' AND `share_type` = ?';
  829. $queryArgs[] = $shareType;
  830. if (isset($shareWith)) {
  831. $where .= ' AND `share_with` = ?';
  832. $queryArgs[] = $shareWith;
  833. }
  834. }
  835. }
  836. if (isset($uidOwner)) {
  837. $where .= ' AND `uid_owner` = ?';
  838. $queryArgs[] = $uidOwner;
  839. if (!isset($shareType)) {
  840. // Prevent unique user targets for group shares from being selected
  841. $where .= ' AND `share_type` != ?';
  842. $queryArgs[] = self::$shareTypeGroupUserUnique;
  843. }
  844. if ($itemType == 'file' || $itemType == 'folder') {
  845. $column = 'file_source';
  846. } else {
  847. $column = 'item_source';
  848. }
  849. } else {
  850. if ($itemType == 'file' || $itemType == 'folder') {
  851. $column = 'file_target';
  852. } else {
  853. $column = 'item_target';
  854. }
  855. }
  856. if (isset($item)) {
  857. if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) {
  858. $where .= ' AND (';
  859. } else {
  860. $where .= ' AND';
  861. }
  862. // If looking for own shared items, check item_source else check item_target
  863. if (isset($uidOwner) || $itemShareWithBySource) {
  864. // If item type is a file, file source needs to be checked in case the item was converted
  865. if ($itemType == 'file' || $itemType == 'folder') {
  866. $where .= ' `file_source` = ?';
  867. $column = 'file_source';
  868. } else {
  869. $where .= ' `item_source` = ?';
  870. $column = 'item_source';
  871. }
  872. } else {
  873. if ($itemType == 'file' || $itemType == 'folder') {
  874. $where .= ' `file_target` = ?';
  875. $item = \OC\Files\Filesystem::normalizePath($item);
  876. } else {
  877. $where .= ' `item_target` = ?';
  878. }
  879. }
  880. $queryArgs[] = $item;
  881. if ($includeCollections && $collectionTypes) {
  882. $placeholders = join(',', array_fill(0, count($collectionTypes), '?'));
  883. $where .= ' OR `item_type` IN ('.$placeholders.'))';
  884. $queryArgs = array_merge($queryArgs, $collectionTypes);
  885. }
  886. }
  887. if ($limit != -1 && !$includeCollections) {
  888. if ($shareType == self::$shareTypeUserAndGroups) {
  889. // Make sure the unique user target is returned if it exists,
  890. // unique targets should follow the group share in the database
  891. // If the limit is not 1, the filtering can be done later
  892. $where .= ' ORDER BY `*PREFIX*share`.`id` DESC';
  893. }
  894. // The limit must be at least 3, because filtering needs to be done
  895. if ($limit < 3) {
  896. $queryLimit = 3;
  897. } else {
  898. $queryLimit = $limit;
  899. }
  900. } else {
  901. $queryLimit = null;
  902. }
  903. // TODO Optimize selects
  904. if ($format == self::FORMAT_STATUSES) {
  905. if ($itemType == 'file' || $itemType == 'folder') {
  906. $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,'
  907. .' `share_type`, `file_source`, `path`, `expiration`, `storage`';
  908. } else {
  909. $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`';
  910. }
  911. } else {
  912. if (isset($uidOwner)) {
  913. if ($itemType == 'file' || $itemType == 'folder') {
  914. $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,'
  915. .' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,'
  916. .' `expiration`, `token`, `storage`';
  917. } else {
  918. $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,'
  919. .' `stime`, `file_source`, `expiration`, `token`';
  920. }
  921. } else {
  922. if ($fileDependent) {
  923. if (($itemType == 'file' || $itemType == 'folder')
  924. && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS
  925. || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT
  926. ) {
  927. $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, '
  928. .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, '
  929. .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, '
  930. .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`';
  931. } else {
  932. $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`,
  933. `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`,
  934. `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`';
  935. }
  936. } else {
  937. $select = '*';
  938. }
  939. }
  940. }
  941. $root = strlen($root);
  942. $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
  943. $result = $query->execute($queryArgs);
  944. if (\OC_DB::isError($result)) {
  945. \OC_Log::write('OCP\Share',
  946. \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where,
  947. \OC_Log::ERROR);
  948. }
  949. $items = array();
  950. $targets = array();
  951. $switchedItems = array();
  952. $mounts = array();
  953. while ($row = $result->fetchRow()) {
  954. if (isset($row['id'])) {
  955. $row['id']=(int)$row['id'];
  956. }
  957. if (isset($row['share_type'])) {
  958. $row['share_type']=(int)$row['share_type'];
  959. }
  960. if (isset($row['parent'])) {
  961. $row['parent']=(int)$row['parent'];
  962. }
  963. if (isset($row['file_parent'])) {
  964. $row['file_parent']=(int)$row['file_parent'];
  965. }
  966. if (isset($row['file_source'])) {
  967. $row['file_source']=(int)$row['file_source'];
  968. }
  969. if (isset($row['permissions'])) {
  970. $row['permissions']=(int)$row['permissions'];
  971. }
  972. if (isset($row['storage'])) {
  973. $row['storage']=(int)$row['storage'];
  974. }
  975. if (isset($row['stime'])) {
  976. $row['stime']=(int)$row['stime'];
  977. }
  978. // Filter out duplicate group shares for users with unique targets
  979. if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) {
  980. $row['share_type'] = self::SHARE_TYPE_GROUP;
  981. $row['share_with'] = $items[$row['parent']]['share_with'];
  982. // Remove the parent group share
  983. unset($items[$row['parent']]);
  984. if ($row['permissions'] == 0) {
  985. continue;
  986. }
  987. } else if (!isset($uidOwner)) {
  988. // Check if the same target already exists
  989. if (isset($targets[$row[$column]])) {
  990. // Check if the same owner shared with the user twice
  991. // through a group and user share - this is allowed
  992. $id = $targets[$row[$column]];
  993. if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) {
  994. // Switch to group share type to ensure resharing conditions aren't bypassed
  995. if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) {
  996. $items[$id]['share_type'] = self::SHARE_TYPE_GROUP;
  997. $items[$id]['share_with'] = $row['share_with'];
  998. }
  999. // Switch ids if sharing permission is granted on only
  1000. // one share to ensure correct parent is used if resharing
  1001. if (~(int)$items[$id]['permissions'] & PERMISSION_SHARE
  1002. && (int)$row['permissions'] & PERMISSION_SHARE) {
  1003. $items[$row['id']] = $items[$id];
  1004. $switchedItems[$id] = $row['id'];
  1005. unset($items[$id]);
  1006. $id = $row['id'];
  1007. }
  1008. // Combine the permissions for the item
  1009. $items[$id]['permissions'] |= (int)$row['permissions'];
  1010. continue;
  1011. }
  1012. } else {
  1013. $targets[$row[$column]] = $row['id'];
  1014. }
  1015. }
  1016. // Remove root from file source paths if retrieving own shared items
  1017. if (isset($uidOwner) && isset($row['path'])) {
  1018. if (isset($row['parent'])) {
  1019. $row['path'] = '/Shared/'.basename($row['path']);
  1020. } else {
  1021. if (!isset($mounts[$row['storage']])) {
  1022. $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']);
  1023. if (is_array($mountPoints)) {
  1024. $mounts[$row['storage']] = current($mountPoints);
  1025. }
  1026. }
  1027. if ($mounts[$row['storage']]) {
  1028. $path = $mounts[$row['storage']]->getMountPoint().$row['path'];
  1029. $row['path'] = substr($path, $root);
  1030. }
  1031. }
  1032. }
  1033. if (isset($row['expiration'])) {
  1034. $time = new \DateTime();
  1035. if ($row['expiration'] < date('Y-m-d H:i', $time->format('U') - $time->getOffset())) {
  1036. self::delete($row['id']);
  1037. continue;
  1038. }
  1039. }
  1040. // Check if resharing is allowed, if not remove share permission
  1041. if (isset($row['permissions']) && !self::isResharingAllowed()) {
  1042. $row['permissions'] &= ~PERMISSION_SHARE;
  1043. }
  1044. // Add display names to result
  1045. if ( isset($row['share_with']) && $row['share_with'] != '') {
  1046. $row['share_with_displayname'] = \OCP\User::getDisplayName($row['share_with']);
  1047. }
  1048. if ( isset($row['uid_owner']) && $row['uid_owner'] != '') {
  1049. $row['displayname_owner'] = \OCP\User::getDisplayName($row['uid_owner']);
  1050. }
  1051. $items[$row['id']] = $row;
  1052. }
  1053. if (!empty($items)) {
  1054. $collectionItems = array();
  1055. foreach ($items as &$row) {
  1056. // Return only the item instead of a 2-dimensional array
  1057. if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) {
  1058. if ($format == self::FORMAT_NONE) {
  1059. return $row;
  1060. } else {
  1061. break;
  1062. }
  1063. }
  1064. // Check if this is a collection of the requested item type
  1065. if ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
  1066. if (($collectionBackend = self::getBackend($row['item_type']))
  1067. && $collectionBackend instanceof Share_Backend_Collection) {
  1068. // Collections can be inside collections, check if the item is a collection
  1069. if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
  1070. $collectionItems[] = $row;
  1071. } else {
  1072. $collection = array();
  1073. $collection['item_type'] = $row['item_type'];
  1074. if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
  1075. $collection['path'] = basename($row['path']);
  1076. }
  1077. $row['collection'] = $collection;
  1078. // Fetch all of the children sources
  1079. $children = $collectionBackend->getChildren($row[$column]);
  1080. foreach ($children as $child) {
  1081. $childItem = $row;
  1082. $childItem['item_type'] = $itemType;
  1083. if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
  1084. $childItem['item_source'] = $child['source'];
  1085. $childItem['item_target'] = $child['target'];
  1086. }
  1087. if ($backend instanceof Share_Backend_File_Dependent) {
  1088. if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
  1089. $childItem['file_source'] = $child['source'];
  1090. } else {
  1091. $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']);
  1092. $childItem['file_source'] = $meta['fileid'];
  1093. }
  1094. $childItem['file_target'] =
  1095. \OC\Files\Filesystem::normalizePath($child['file_path']);
  1096. }
  1097. if (isset($item)) {
  1098. if ($childItem[$column] == $item) {
  1099. // Return only the item instead of a 2-dimensional array
  1100. if ($limit == 1) {
  1101. if ($format == self::FORMAT_NONE) {
  1102. return $childItem;
  1103. } else {
  1104. // Unset the items array and break out of both loops
  1105. $items = array();
  1106. $items[] = $childItem;
  1107. break 2;
  1108. }
  1109. } else {
  1110. $collectionItems[] = $childItem;
  1111. }
  1112. }
  1113. } else {
  1114. $collectionItems[] = $childItem;
  1115. }
  1116. }
  1117. }
  1118. }
  1119. // Remove collection item
  1120. $toRemove = $row['id'];
  1121. if (array_key_exists($toRemove, $switchedItems)) {
  1122. $toRemove = $switchedItems[$toRemove];
  1123. }
  1124. unset($items[$toRemove]);
  1125. }
  1126. }
  1127. if (!empty($collectionItems)) {
  1128. $items = array_merge($items, $collectionItems);
  1129. }
  1130. if (empty($items) && $limit == 1) {
  1131. return false;
  1132. }
  1133. if ($format == self::FORMAT_NONE) {
  1134. return $items;
  1135. } else if ($format == self::FORMAT_STATUSES) {
  1136. $statuses = array();
  1137. foreach ($items as $item) {
  1138. if ($item['share_type'] == self::SHARE_TYPE_LINK) {
  1139. $statuses[$item[$column]]['link'] = true;
  1140. } else if (!isset($statuses[$item[$column]])) {
  1141. $statuses[$item[$column]]['link'] = false;
  1142. }
  1143. if ($itemType == 'file' || $itemType == 'folder') {
  1144. $statuses[$item[$column]]['path'] = $item['path'];
  1145. }
  1146. }
  1147. return $statuses;
  1148. } else {
  1149. return $backend->formatItems($items, $format, $parameters);
  1150. }
  1151. } else if ($limit == 1 || (isset($uidOwner) && isset($item))) {
  1152. return false;
  1153. }
  1154. return array();
  1155. }
  1156. /**
  1157. * @brief Put shared item into the database
  1158. * @param string Item type
  1159. * @param string Item source
  1160. * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_LINK
  1161. * @param string User or group the item is being shared with
  1162. * @param int CRUDS permissions
  1163. * @param bool|array Parent folder target (optional)
  1164. * @return bool Returns true on success or false on failure
  1165. */
  1166. private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner,
  1167. $permissions, $parentFolder = null, $token = null) {
  1168. $backend = self::getBackend($itemType);
  1169. // Check if this is a reshare
  1170. if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) {
  1171. // Check if attempting to share back to owner
  1172. if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
  1173. $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is the original sharer';
  1174. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  1175. throw new \Exception($message);
  1176. }
  1177. // Check if share permissions is granted
  1178. if (self::isResharingAllowed() && (int)$checkReshare['permissions'] & PERMISSION_SHARE) {
  1179. if (~(int)$checkReshare['permissions'] & $permissions) {
  1180. $message = 'Sharing '.$itemSource
  1181. .' failed, because the permissions exceed permissions granted to '.$uidOwner;
  1182. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  1183. throw new \Exception($message);
  1184. } else {
  1185. // TODO Don't check if inside folder
  1186. $parent = $checkReshare['id'];
  1187. $itemSource = $checkReshare['item_source'];
  1188. $fileSource = $checkReshare['file_source'];
  1189. $suggestedItemTarget = $checkReshare['item_target'];
  1190. $suggestedFileTarget = $checkReshare['file_target'];
  1191. $filePath = $checkReshare['file_target'];
  1192. }
  1193. } else {
  1194. $message = 'Sharing '.$itemSource.' failed, because resharing is not allowed';
  1195. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  1196. throw new \Exception($message);
  1197. }
  1198. } else {
  1199. $parent = null;
  1200. $suggestedItemTarget = null;
  1201. $suggestedFileTarget = null;
  1202. if (!$backend->isValidSource($itemSource, $uidOwner)) {
  1203. $message = 'Sharing '.$itemSource.' failed, because the sharing backend for '
  1204. .$itemType.' could not find its source';
  1205. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  1206. throw new \Exception($message);
  1207. }
  1208. $parent = null;
  1209. if ($backend instanceof Share_Backend_File_Dependent) {
  1210. $filePath = $backend->getFilePath($itemSource, $uidOwner);
  1211. if ($itemType == 'file' || $itemType == 'folder') {
  1212. $fileSource = $itemSource;
  1213. } else {
  1214. $meta = \OC\Files\Filesystem::getFileInfo($filePath);
  1215. $fileSource = $meta['fileid'];
  1216. }
  1217. if ($fileSource == -1) {
  1218. $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache';
  1219. \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
  1220. throw new \Exception($message);
  1221. }
  1222. } else {
  1223. $filePath = null;
  1224. $fileSource = null;
  1225. }
  1226. }
  1227. $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,'
  1228. .' `parent`, `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,'
  1229. .' `file_target`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
  1230. // Share with a group
  1231. if ($shareType == self::SHARE_TYPE_GROUP) {
  1232. $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'],
  1233. $uidOwner, $suggestedItemTarget);
  1234. \OC_Hook::emit('OCP\Share', 'pre_shared', array(
  1235. 'itemType' => $itemType,
  1236. 'itemSource' => $itemSource,
  1237. 'itemTarget' => $groupItemTarget,
  1238. 'shareType' => $shareType,
  1239. 'shareWith' => $shareWith['group'],
  1240. 'uidOwner' => $uidOwner,
  1241. 'permissions' => $permissions,
  1242. 'fileSource' => $fileSource,
  1243. 'token' => $token
  1244. ));
  1245. if (isset($fileSource)) {
  1246. if ($parentFolder) {
  1247. if ($parentFolder === true) {
  1248. $groupFileTarget = self::generateTarget('file', $filePath, $shareType,
  1249. $shareWith['group'], $uidOwner, $suggestedFileTarget);
  1250. // Set group default file target for future use
  1251. $parentFolders[0]['folder'] = $groupFileTarget;
  1252. } else {
  1253. // Get group default file target
  1254. $groupFileTarget = $parentFolder[0]['folder'].$itemSource;
  1255. $parent = $parentFolder[0]['id'];
  1256. }
  1257. } else {
  1258. $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'],
  1259. $uidOwner, $suggestedFileTarget);
  1260. }
  1261. } else {
  1262. $groupFileTarget = null;
  1263. }
  1264. $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType,
  1265. $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token));
  1266. // Save this id, any extra rows for this group share will need to reference it
  1267. $parent = \OC_DB::insertid('*PREFIX*…

Large files files are truncated, but you can click here to view the full file