storage.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. /**
  3. * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace OCA\Files_Sharing\External;
  9. use OC\Files\Filesystem;
  10. use OC\Files\Storage\DAV;
  11. use OC\ForbiddenException;
  12. use OCA\Files_Sharing\ISharedStorage;
  13. use OCP\Files\NotFoundException;
  14. use OCP\Files\StorageInvalidException;
  15. use OCP\Files\StorageNotAvailableException;
  16. class Storage extends DAV implements ISharedStorage {
  17. /**
  18. * @var string
  19. */
  20. private $remoteUser;
  21. /**
  22. * @var string
  23. */
  24. private $remote;
  25. /**
  26. * @var string
  27. */
  28. private $mountPoint;
  29. /**
  30. * @var string
  31. */
  32. private $token;
  33. /**
  34. * @var \OCP\ICertificateManager
  35. */
  36. private $certificateManager;
  37. private $updateChecked = false;
  38. /**
  39. * @var \OCA\Files_Sharing\External\Manager
  40. */
  41. private $manager;
  42. public function __construct($options) {
  43. $this->manager = $options['manager'];
  44. $this->certificateManager = $options['certificateManager'];
  45. $this->remote = $options['remote'];
  46. $this->remoteUser = $options['owner'];
  47. list($protocol, $remote) = explode('://', $this->remote);
  48. if (strpos($remote, '/')) {
  49. list($host, $root) = explode('/', $remote, 2);
  50. } else {
  51. $host = $remote;
  52. $root = '';
  53. }
  54. $secure = $protocol === 'https';
  55. $root = rtrim($root, '/') . '/public.php/webdav';
  56. $this->mountPoint = $options['mountpoint'];
  57. $this->token = $options['token'];
  58. parent::__construct(array(
  59. 'secure' => $secure,
  60. 'host' => $host,
  61. 'root' => $root,
  62. 'user' => $options['token'],
  63. 'password' => $options['password']
  64. ));
  65. }
  66. public function getRemoteUser() {
  67. return $this->remoteUser;
  68. }
  69. public function getRemote() {
  70. return $this->remote;
  71. }
  72. public function getMountPoint() {
  73. return $this->mountPoint;
  74. }
  75. public function getToken() {
  76. return $this->token;
  77. }
  78. public function getPassword() {
  79. return $this->password;
  80. }
  81. /**
  82. * @brief get id of the mount point
  83. * @return string
  84. */
  85. public function getId() {
  86. return 'shared::' . md5($this->token . '@' . $this->remote);
  87. }
  88. public function getCache($path = '', $storage = null) {
  89. if (!$storage) {
  90. $this->cache = new Cache($this, $this->remote, $this->remoteUser);
  91. }
  92. return $this->cache;
  93. }
  94. /**
  95. * @param string $path
  96. * @param \OC\Files\Storage\Storage $storage
  97. * @return \OCA\Files_Sharing\External\Scanner
  98. */
  99. public function getScanner($path = '', $storage = null) {
  100. if (!$storage) {
  101. $storage = $this;
  102. }
  103. if (!isset($this->scanner)) {
  104. $this->scanner = new Scanner($storage);
  105. }
  106. return $this->scanner;
  107. }
  108. /**
  109. * check if a file or folder has been updated since $time
  110. *
  111. * @param string $path
  112. * @param int $time
  113. * @throws \OCP\Files\StorageNotAvailableException
  114. * @throws \OCP\Files\StorageInvalidException
  115. * @return bool
  116. */
  117. public function hasUpdated($path, $time) {
  118. // since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage
  119. // because of that we only do one check for the entire storage per request
  120. if ($this->updateChecked) {
  121. return false;
  122. }
  123. $this->updateChecked = true;
  124. try {
  125. return parent::hasUpdated('', $time);
  126. } catch (StorageNotAvailableException $e) {
  127. // see if we can find out why the share is unavailable\
  128. try {
  129. $this->getShareInfo();
  130. } catch (NotFoundException $shareException) {
  131. // a 404 can either mean that the share no longer exists or there is no ownCloud on the remote
  132. if ($this->testRemote()) {
  133. // valid ownCloud instance means that the public share no longer exists
  134. // since this is permanent (re-sharing the file will create a new token)
  135. // we remove the invalid storage
  136. $this->manager->removeShare($this->mountPoint);
  137. $this->manager->getMountManager()->removeMount($this->mountPoint);
  138. throw new StorageInvalidException();
  139. } else {
  140. // ownCloud instance is gone, likely to be a temporary server configuration error
  141. throw $e;
  142. }
  143. } catch (\Exception $shareException) {
  144. // todo, maybe handle 403 better and ask the user for a new password
  145. throw $e;
  146. }
  147. throw $e;
  148. }
  149. }
  150. /**
  151. * check if the configured remote is a valid ownCloud instance
  152. *
  153. * @return bool
  154. */
  155. protected function testRemote() {
  156. try {
  157. $result = file_get_contents($this->remote . '/status.php');
  158. $data = json_decode($result);
  159. return is_object($data) and !empty($data->version);
  160. } catch (\Exception $e) {
  161. return false;
  162. }
  163. }
  164. public function getShareInfo() {
  165. $remote = $this->getRemote();
  166. $token = $this->getToken();
  167. $password = $this->getPassword();
  168. $url = $remote . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
  169. $ch = curl_init();
  170. curl_setopt($ch, CURLOPT_URL, $url);
  171. curl_setopt($ch, CURLOPT_POST, 1);
  172. curl_setopt($ch, CURLOPT_POSTFIELDS,
  173. http_build_query(array('password' => $password)));
  174. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  175. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
  176. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  177. $path = $this->certificateManager->getCertificateBundle();
  178. if (is_readable($path)) {
  179. curl_setopt($ch, CURLOPT_CAINFO, $path);
  180. }
  181. $result = curl_exec($ch);
  182. $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  183. $errorMessage = curl_error($ch);
  184. curl_close($ch);
  185. if (!empty($errorMessage)) {
  186. throw new \Exception($errorMessage);
  187. }
  188. switch ($status) {
  189. case 401:
  190. case 403:
  191. throw new ForbiddenException();
  192. case 404:
  193. throw new NotFoundException();
  194. case 500:
  195. throw new \Exception();
  196. }
  197. return json_decode($result, true);
  198. }
  199. }