You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
10 KiB

13 years ago
11 years ago
13 years ago
11 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. <?php
  2. /**
  3. * Copyright (c) 2012 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 OC\Files\Storage;
  9. use OC\Files\Filesystem;
  10. use OC\Files\Cache\Watcher;
  11. /**
  12. * Storage backend class for providing common filesystem operation methods
  13. * which are not storage-backend specific.
  14. *
  15. * \OC\Files\Storage\Common is never used directly; it is extended by all other
  16. * storage backends, where its methods may be overridden, and additional
  17. * (backend-specific) methods are defined.
  18. *
  19. * Some \OC\Files\Storage\Common methods call functions which are first defined
  20. * in classes which extend it, e.g. $this->stat() .
  21. */
  22. abstract class Common implements \OC\Files\Storage\Storage {
  23. protected $cache;
  24. protected $scanner;
  25. protected $permissioncache;
  26. protected $watcher;
  27. protected $storageCache;
  28. /**
  29. * @var string[]
  30. */
  31. protected $cachedFiles = array();
  32. public function __construct($parameters) {
  33. }
  34. /**
  35. * Remove a file or folder
  36. *
  37. * @param string $path
  38. * @return bool
  39. */
  40. protected function remove($path) {
  41. if ($this->is_dir($path)) {
  42. return $this->rmdir($path);
  43. } else if ($this->is_file($path)) {
  44. return $this->unlink($path);
  45. } else {
  46. return false;
  47. }
  48. }
  49. public function is_dir($path) {
  50. return $this->filetype($path) === 'dir';
  51. }
  52. public function is_file($path) {
  53. return $this->filetype($path) === 'file';
  54. }
  55. public function filesize($path) {
  56. if ($this->is_dir($path)) {
  57. return 0; //by definition
  58. } else {
  59. $stat = $this->stat($path);
  60. if (isset($stat['size'])) {
  61. return $stat['size'];
  62. } else {
  63. return 0;
  64. }
  65. }
  66. }
  67. public function isReadable($path) {
  68. // at least check whether it exists
  69. // subclasses might want to implement this more thoroughly
  70. return $this->file_exists($path);
  71. }
  72. public function isUpdatable($path) {
  73. // at least check whether it exists
  74. // subclasses might want to implement this more thoroughly
  75. // a non-existing file/folder isn't updatable
  76. return $this->file_exists($path);
  77. }
  78. public function isCreatable($path) {
  79. if ($this->is_dir($path) && $this->isUpdatable($path)) {
  80. return true;
  81. }
  82. return false;
  83. }
  84. public function isDeletable($path) {
  85. if ($path === '' || $path === '/') {
  86. return false;
  87. }
  88. $parent = dirname($path);
  89. return $this->isUpdatable($parent) && $this->isUpdatable($path);
  90. }
  91. public function isSharable($path) {
  92. if (\OC_Util::isSharingDisabledForUser()) {
  93. return false;
  94. }
  95. return $this->isReadable($path);
  96. }
  97. public function getPermissions($path) {
  98. $permissions = 0;
  99. if ($this->isCreatable($path)) {
  100. $permissions |= \OCP\Constants::PERMISSION_CREATE;
  101. }
  102. if ($this->isReadable($path)) {
  103. $permissions |= \OCP\Constants::PERMISSION_READ;
  104. }
  105. if ($this->isUpdatable($path)) {
  106. $permissions |= \OCP\Constants::PERMISSION_UPDATE;
  107. }
  108. if ($this->isDeletable($path)) {
  109. $permissions |= \OCP\Constants::PERMISSION_DELETE;
  110. }
  111. if ($this->isSharable($path)) {
  112. $permissions |= \OCP\Constants::PERMISSION_SHARE;
  113. }
  114. return $permissions;
  115. }
  116. public function filemtime($path) {
  117. $stat = $this->stat($path);
  118. if (isset($stat['mtime']) && $stat['mtime'] > 0) {
  119. return $stat['mtime'];
  120. } else {
  121. return 0;
  122. }
  123. }
  124. public function file_get_contents($path) {
  125. $handle = $this->fopen($path, "r");
  126. if (!$handle) {
  127. return false;
  128. }
  129. $data = stream_get_contents($handle);
  130. fclose($handle);
  131. return $data;
  132. }
  133. public function file_put_contents($path, $data) {
  134. $handle = $this->fopen($path, "w");
  135. $this->removeCachedFile($path);
  136. $count = fwrite($handle, $data);
  137. fclose($handle);
  138. return $count;
  139. }
  140. public function rename($path1, $path2) {
  141. $this->remove($path2);
  142. $this->removeCachedFile($path1);
  143. return $this->copy($path1, $path2) and $this->remove($path1);
  144. }
  145. public function copy($path1, $path2) {
  146. if ($this->is_dir($path1)) {
  147. $this->remove($path2);
  148. $dir = $this->opendir($path1);
  149. $this->mkdir($path2);
  150. while ($file = readdir($dir)) {
  151. if (!Filesystem::isIgnoredDir($file)) {
  152. if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
  153. return false;
  154. }
  155. }
  156. }
  157. closedir($dir);
  158. return true;
  159. } else {
  160. $source = $this->fopen($path1, 'r');
  161. $target = $this->fopen($path2, 'w');
  162. list(, $result) = \OC_Helper::streamCopy($source, $target);
  163. $this->removeCachedFile($path2);
  164. return $result;
  165. }
  166. }
  167. public function getMimeType($path) {
  168. if ($this->is_dir($path)) {
  169. return 'httpd/unix-directory';
  170. } elseif ($this->file_exists($path)) {
  171. return \OC_Helper::getFileNameMimeType($path);
  172. } else {
  173. return false;
  174. }
  175. }
  176. public function hash($type, $path, $raw = false) {
  177. $fh = $this->fopen($path, 'rb');
  178. $ctx = hash_init($type);
  179. hash_update_stream($ctx, $fh);
  180. fclose($fh);
  181. return hash_final($ctx, $raw);
  182. }
  183. public function search($query) {
  184. return $this->searchInDir($query);
  185. }
  186. public function getLocalFile($path) {
  187. return $this->getCachedFile($path);
  188. }
  189. /**
  190. * @param string $path
  191. * @return string
  192. */
  193. protected function toTmpFile($path) { //no longer in the storage api, still useful here
  194. $source = $this->fopen($path, 'r');
  195. if (!$source) {
  196. return false;
  197. }
  198. if ($pos = strrpos($path, '.')) {
  199. $extension = substr($path, $pos);
  200. } else {
  201. $extension = '';
  202. }
  203. $tmpFile = \OC_Helper::tmpFile($extension);
  204. $target = fopen($tmpFile, 'w');
  205. \OC_Helper::streamCopy($source, $target);
  206. fclose($target);
  207. return $tmpFile;
  208. }
  209. public function getLocalFolder($path) {
  210. $baseDir = \OC_Helper::tmpFolder();
  211. $this->addLocalFolder($path, $baseDir);
  212. return $baseDir;
  213. }
  214. /**
  215. * @param string $path
  216. * @param string $target
  217. */
  218. private function addLocalFolder($path, $target) {
  219. $dh = $this->opendir($path);
  220. if (is_resource($dh)) {
  221. while (($file = readdir($dh)) !== false) {
  222. if ($file !== '.' and $file !== '..') {
  223. if ($this->is_dir($path . '/' . $file)) {
  224. mkdir($target . '/' . $file);
  225. $this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
  226. } else {
  227. $tmp = $this->toTmpFile($path . '/' . $file);
  228. rename($tmp, $target . '/' . $file);
  229. }
  230. }
  231. }
  232. }
  233. }
  234. /**
  235. * @param string $query
  236. */
  237. protected function searchInDir($query, $dir = '') {
  238. $files = array();
  239. $dh = $this->opendir($dir);
  240. if (is_resource($dh)) {
  241. while (($item = readdir($dh)) !== false) {
  242. if ($item == '.' || $item == '..') continue;
  243. if (strstr(strtolower($item), strtolower($query)) !== false) {
  244. $files[] = $dir . '/' . $item;
  245. }
  246. if ($this->is_dir($dir . '/' . $item)) {
  247. $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
  248. }
  249. }
  250. }
  251. closedir($dh);
  252. return $files;
  253. }
  254. /**
  255. * check if a file or folder has been updated since $time
  256. *
  257. * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
  258. * the mtime should always return false here. As a result storage implementations that always return false expect
  259. * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
  260. * ownClouds filesystem.
  261. *
  262. * @param string $path
  263. * @param int $time
  264. * @return bool
  265. */
  266. public function hasUpdated($path, $time) {
  267. return $this->filemtime($path) > $time;
  268. }
  269. public function getCache($path = '', $storage = null) {
  270. if (!$storage) {
  271. $storage = $this;
  272. }
  273. if (!isset($this->cache)) {
  274. $this->cache = new \OC\Files\Cache\Cache($storage);
  275. }
  276. return $this->cache;
  277. }
  278. public function getScanner($path = '', $storage = null) {
  279. if (!$storage) {
  280. $storage = $this;
  281. }
  282. if (!isset($this->scanner)) {
  283. $this->scanner = new \OC\Files\Cache\Scanner($storage);
  284. }
  285. return $this->scanner;
  286. }
  287. public function getWatcher($path = '', $storage = null) {
  288. if (!$storage) {
  289. $storage = $this;
  290. }
  291. if (!isset($this->watcher)) {
  292. $this->watcher = new \OC\Files\Cache\Watcher($storage);
  293. $this->watcher->setPolicy(\OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_ONCE));
  294. }
  295. return $this->watcher;
  296. }
  297. public function getStorageCache($storage = null) {
  298. if (!$storage) {
  299. $storage = $this;
  300. }
  301. if (!isset($this->storageCache)) {
  302. $this->storageCache = new \OC\Files\Cache\Storage($storage);
  303. }
  304. return $this->storageCache;
  305. }
  306. /**
  307. * get the owner of a path
  308. *
  309. * @param string $path The path to get the owner
  310. * @return string|false uid or false
  311. */
  312. public function getOwner($path) {
  313. return \OC_User::getUser();
  314. }
  315. /**
  316. * get the ETag for a file or folder
  317. *
  318. * @param string $path
  319. * @return string|false
  320. */
  321. public function getETag($path) {
  322. $ETagFunction = \OC_Connector_Sabre_Node::$ETagFunction;
  323. if ($ETagFunction) {
  324. $hash = call_user_func($ETagFunction, $path);
  325. return $hash;
  326. } else {
  327. return uniqid();
  328. }
  329. }
  330. /**
  331. * clean a path, i.e. remove all redundant '.' and '..'
  332. * making sure that it can't point to higher than '/'
  333. *
  334. * @param string $path The path to clean
  335. * @return string cleaned path
  336. */
  337. public function cleanPath($path) {
  338. if (strlen($path) == 0 or $path[0] != '/') {
  339. $path = '/' . $path;
  340. }
  341. $output = array();
  342. foreach (explode('/', $path) as $chunk) {
  343. if ($chunk == '..') {
  344. array_pop($output);
  345. } else if ($chunk == '.') {
  346. } else {
  347. $output[] = $chunk;
  348. }
  349. }
  350. return implode('/', $output);
  351. }
  352. public function test() {
  353. if ($this->stat('')) {
  354. return true;
  355. }
  356. return false;
  357. }
  358. /**
  359. * get the free space in the storage
  360. *
  361. * @param string $path
  362. * @return int|false
  363. */
  364. public function free_space($path) {
  365. return \OCP\Files\FileInfo::SPACE_UNKNOWN;
  366. }
  367. /**
  368. * {@inheritdoc}
  369. */
  370. public function isLocal() {
  371. // the common implementation returns a temporary file by
  372. // default, which is not local
  373. return false;
  374. }
  375. /**
  376. * @param string $path
  377. */
  378. protected function getCachedFile($path) {
  379. if (!isset($this->cachedFiles[$path])) {
  380. $this->cachedFiles[$path] = $this->toTmpFile($path);
  381. }
  382. return $this->cachedFiles[$path];
  383. }
  384. protected function removeCachedFile($path) {
  385. unset($this->cachedFiles[$path]);
  386. }
  387. /**
  388. * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
  389. *
  390. * @param string $class
  391. * @return bool
  392. */
  393. public function instanceOfStorage($class) {
  394. return is_a($this, $class);
  395. }
  396. /**
  397. * A custom storage implementation can return an url for direct download of a give file.
  398. *
  399. * For now the returned array can hold the parameter url - in future more attributes might follow.
  400. *
  401. * @param string $path
  402. * @return array|false
  403. */
  404. public function getDirectDownload($path) {
  405. return [];
  406. }
  407. }