manager.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. <?php
  2. /**
  3. * @author Björn Schießle <schiessle@owncloud.com>
  4. * @author Joas Schilling <nickvergessen@owncloud.com>
  5. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  6. * @author Morris Jobke <hey@morrisjobke.de>
  7. * @author Robin Appelman <icewind@owncloud.com>
  8. * @author Vincent Petry <pvince81@owncloud.com>
  9. *
  10. * @copyright Copyright (c) 2015, ownCloud, Inc.
  11. * @license AGPL-3.0
  12. *
  13. * This code is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License, version 3,
  15. * as published by the Free Software Foundation.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU Affero General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU Affero General Public License, version 3,
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>
  24. *
  25. */
  26. namespace OCA\Files_Sharing\External;
  27. use OC\Files\Filesystem;
  28. use OCP\Files;
  29. class Manager {
  30. const STORAGE = '\OCA\Files_Sharing\External\Storage';
  31. /**
  32. * @var string
  33. */
  34. private $uid;
  35. /**
  36. * @var \OCP\IDBConnection
  37. */
  38. private $connection;
  39. /**
  40. * @var \OC\Files\Mount\Manager
  41. */
  42. private $mountManager;
  43. /**
  44. * @var \OCP\Files\Storage\IStorageFactory
  45. */
  46. private $storageLoader;
  47. /**
  48. * @var \OC\HTTPHelper
  49. */
  50. private $httpHelper;
  51. /**
  52. * @param \OCP\IDBConnection $connection
  53. * @param \OC\Files\Mount\Manager $mountManager
  54. * @param \OCP\Files\Storage\IStorageFactory $storageLoader
  55. * @param \OC\HTTPHelper $httpHelper
  56. * @param string $uid
  57. */
  58. public function __construct(\OCP\IDBConnection $connection, \OC\Files\Mount\Manager $mountManager,
  59. \OCP\Files\Storage\IStorageFactory $storageLoader, \OC\HTTPHelper $httpHelper, $uid) {
  60. $this->connection = $connection;
  61. $this->mountManager = $mountManager;
  62. $this->storageLoader = $storageLoader;
  63. $this->httpHelper = $httpHelper;
  64. $this->uid = $uid;
  65. }
  66. /**
  67. * add new server-to-server share
  68. *
  69. * @param string $remote
  70. * @param string $token
  71. * @param string $password
  72. * @param string $name
  73. * @param string $owner
  74. * @param boolean $accepted
  75. * @param string $user
  76. * @param int $remoteId
  77. * @return Mount|null
  78. */
  79. public function addShare($remote, $token, $password, $name, $owner, $accepted=false, $user = null, $remoteId = -1) {
  80. $user = $user ? $user : $this->uid;
  81. $accepted = $accepted ? 1 : 0;
  82. $name = Filesystem::normalizePath('/' . $name);
  83. if (!$accepted) {
  84. // To avoid conflicts with the mount point generation later,
  85. // we only use a temporary mount point name here. The real
  86. // mount point name will be generated when accepting the share,
  87. // using the original share item name.
  88. $tmpMountPointName = '{{TemporaryMountPointName#' . $name . '}}';
  89. $mountPoint = $tmpMountPointName;
  90. $hash = md5($tmpMountPointName);
  91. $data = [
  92. 'remote' => $remote,
  93. 'share_token' => $token,
  94. 'password' => $password,
  95. 'name' => $name,
  96. 'owner' => $owner,
  97. 'user' => $user,
  98. 'mountpoint' => $mountPoint,
  99. 'mountpoint_hash' => $hash,
  100. 'accepted' => $accepted,
  101. 'remote_id' => $remoteId,
  102. ];
  103. $i = 1;
  104. while (!$this->connection->insertIfNotExist('*PREFIX*share_external', $data, ['user', 'mountpoint_hash'])) {
  105. // The external share already exists for the user
  106. $data['mountpoint'] = $tmpMountPointName . '-' . $i;
  107. $data['mountpoint_hash'] = md5($data['mountpoint']);
  108. $i++;
  109. }
  110. return null;
  111. }
  112. $mountPoint = Files::buildNotExistingFileName('/', $name);
  113. $mountPoint = Filesystem::normalizePath('/' . $mountPoint);
  114. $hash = md5($mountPoint);
  115. $query = $this->connection->prepare('
  116. INSERT INTO `*PREFIX*share_external`
  117. (`remote`, `share_token`, `password`, `name`, `owner`, `user`, `mountpoint`, `mountpoint_hash`, `accepted`, `remote_id`)
  118. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  119. ');
  120. $query->execute(array($remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId));
  121. $options = array(
  122. 'remote' => $remote,
  123. 'token' => $token,
  124. 'password' => $password,
  125. 'mountpoint' => $mountPoint,
  126. 'owner' => $owner
  127. );
  128. return $this->mountShare($options);
  129. }
  130. private function setupMounts() {
  131. // don't setup server-to-server shares if the admin disabled it
  132. if (\OCA\Files_Sharing\Helper::isIncomingServer2serverShareEnabled() === false) {
  133. return false;
  134. }
  135. if (!is_null($this->uid)) {
  136. $query = $this->connection->prepare('
  137. SELECT `remote`, `share_token`, `password`, `mountpoint`, `owner`
  138. FROM `*PREFIX*share_external`
  139. WHERE `user` = ? AND `accepted` = ?
  140. ');
  141. $query->execute(array($this->uid, 1));
  142. while ($row = $query->fetch()) {
  143. $row['manager'] = $this;
  144. $row['token'] = $row['share_token'];
  145. $this->mountShare($row);
  146. }
  147. }
  148. }
  149. /**
  150. * get share
  151. *
  152. * @param int $id share id
  153. * @return mixed share of false
  154. */
  155. private function getShare($id) {
  156. $getShare = $this->connection->prepare('
  157. SELECT `remote`, `share_token`, `name`
  158. FROM `*PREFIX*share_external`
  159. WHERE `id` = ? AND `user` = ?');
  160. $result = $getShare->execute(array($id, $this->uid));
  161. return $result ? $getShare->fetch() : false;
  162. }
  163. /**
  164. * accept server-to-server share
  165. *
  166. * @param int $id
  167. */
  168. public function acceptShare($id) {
  169. $share = $this->getShare($id);
  170. if ($share) {
  171. $mountPoint = Files::buildNotExistingFileName('/', $share['name']);
  172. $mountPoint = Filesystem::normalizePath('/' . $mountPoint);
  173. $hash = md5($mountPoint);
  174. $acceptShare = $this->connection->prepare('
  175. UPDATE `*PREFIX*share_external`
  176. SET `accepted` = ?,
  177. `mountpoint` = ?,
  178. `mountpoint_hash` = ?
  179. WHERE `id` = ? AND `user` = ?');
  180. $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid));
  181. $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $id, 'accept');
  182. }
  183. }
  184. /**
  185. * decline server-to-server share
  186. *
  187. * @param int $id
  188. */
  189. public function declineShare($id) {
  190. $share = $this->getShare($id);
  191. if ($share) {
  192. $removeShare = $this->connection->prepare('
  193. DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
  194. $removeShare->execute(array($id, $this->uid));
  195. $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $id, 'decline');
  196. }
  197. }
  198. /**
  199. * inform remote server whether server-to-server share was accepted/declined
  200. *
  201. * @param string $remote
  202. * @param string $token
  203. * @param int $id
  204. * @param string $feedback
  205. * @return boolean
  206. */
  207. private function sendFeedbackToRemote($remote, $token, $id, $feedback) {
  208. $url = $remote . \OCP\Share::BASE_PATH_TO_SHARE_API . '/' . $id . '/' . $feedback . '?format=' . \OCP\Share::RESPONSE_FORMAT;
  209. $fields = array('token' => $token);
  210. $result = $this->httpHelper->post($url, $fields);
  211. $status = json_decode($result['result'], true);
  212. return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100);
  213. }
  214. /**
  215. * setup the server-to-server mounts
  216. *
  217. * @param array $params
  218. */
  219. public static function setup(array $params) {
  220. $externalManager = new \OCA\Files_Sharing\External\Manager(
  221. \OC::$server->getDatabaseConnection(),
  222. \OC\Files\Filesystem::getMountManager(),
  223. \OC\Files\Filesystem::getLoader(),
  224. \OC::$server->getHTTPHelper(),
  225. $params['user']
  226. );
  227. $externalManager->setupMounts();
  228. }
  229. /**
  230. * remove '/user/files' from the path and trailing slashes
  231. *
  232. * @param string $path
  233. * @return string
  234. */
  235. protected function stripPath($path) {
  236. $prefix = '/' . $this->uid . '/files';
  237. return rtrim(substr($path, strlen($prefix)), '/');
  238. }
  239. /**
  240. * @param array $data
  241. * @return Mount
  242. */
  243. protected function mountShare($data) {
  244. $data['manager'] = $this;
  245. $mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
  246. $data['mountpoint'] = $mountPoint;
  247. $data['certificateManager'] = \OC::$server->getCertificateManager($this->uid);
  248. $mount = new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
  249. $this->mountManager->addMount($mount);
  250. return $mount;
  251. }
  252. /**
  253. * @return \OC\Files\Mount\Manager
  254. */
  255. public function getMountManager() {
  256. return $this->mountManager;
  257. }
  258. /**
  259. * @param string $source
  260. * @param string $target
  261. * @return bool
  262. */
  263. public function setMountPoint($source, $target) {
  264. $source = $this->stripPath($source);
  265. $target = $this->stripPath($target);
  266. $sourceHash = md5($source);
  267. $targetHash = md5($target);
  268. $query = $this->connection->prepare('
  269. UPDATE `*PREFIX*share_external`
  270. SET `mountpoint` = ?, `mountpoint_hash` = ?
  271. WHERE `mountpoint_hash` = ?
  272. AND `user` = ?
  273. ');
  274. $result = (bool)$query->execute(array($target, $targetHash, $sourceHash, $this->uid));
  275. return $result;
  276. }
  277. public function removeShare($mountPoint) {
  278. $mountPoint = $this->stripPath($mountPoint);
  279. $hash = md5($mountPoint);
  280. $getShare = $this->connection->prepare('
  281. SELECT `remote`, `share_token`, `remote_id`
  282. FROM `*PREFIX*share_external`
  283. WHERE `mountpoint_hash` = ? AND `user` = ?');
  284. $result = $getShare->execute(array($hash, $this->uid));
  285. if ($result) {
  286. $share = $getShare->fetch();
  287. $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
  288. }
  289. $query = $this->connection->prepare('
  290. DELETE FROM `*PREFIX*share_external`
  291. WHERE `mountpoint_hash` = ?
  292. AND `user` = ?
  293. ');
  294. return (bool)$query->execute(array($hash, $this->uid));
  295. }
  296. /**
  297. * remove all shares for user $uid if the user was deleted
  298. *
  299. * @param string $uid
  300. * @return bool
  301. */
  302. public function removeUserShares($uid) {
  303. $getShare = $this->connection->prepare('
  304. SELECT `remote`, `share_token`, `remote_id`
  305. FROM `*PREFIX*share_external`
  306. WHERE `user` = ?');
  307. $result = $getShare->execute(array($uid));
  308. if ($result) {
  309. $shares = $getShare->fetchAll();
  310. foreach($shares as $share) {
  311. $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
  312. }
  313. }
  314. $query = $this->connection->prepare('
  315. DELETE FROM `*PREFIX*share_external`
  316. WHERE `user` = ?
  317. ');
  318. return (bool)$query->execute(array($uid));
  319. }
  320. /**
  321. * return a list of shares which are not yet accepted by the user
  322. *
  323. * @return array list of open server-to-server shares
  324. */
  325. public function getOpenShares() {
  326. return $this->getShares(false);
  327. }
  328. /**
  329. * return a list of shares for the user
  330. *
  331. * @param bool|null $accepted True for accepted only,
  332. * false for not accepted,
  333. * null for all shares of the user
  334. * @return array list of open server-to-server shares
  335. */
  336. private function getShares($accepted) {
  337. $query = 'SELECT * FROM `*PREFIX*share_external` WHERE `user` = ?';
  338. $parameters = [$this->uid];
  339. if (!is_null($accepted)) {
  340. $query .= ' AND `accepted` = ?';
  341. $parameters[] = (int) $accepted;
  342. }
  343. $query .= ' ORDER BY `id` ASC';
  344. $shares = $this->connection->prepare($query);
  345. $result = $shares->execute($parameters);
  346. return $result ? $shares->fetchAll() : [];
  347. }
  348. }