PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/apps/files_external/lib/swift.php

https://github.com/sezuan/core
PHP | 587 lines | 521 code | 41 blank | 25 comment | 45 complexity | 5d59da5af0880acfb6f521d9fc0652f0 MD5 | raw file
Possible License(s): AGPL-3.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
  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. require_once 'php-cloudfiles/cloudfiles.php';
  10. class SWIFT extends \OC\Files\Storage\Common{
  11. private $id;
  12. private $host;
  13. private $root;
  14. private $user;
  15. private $token;
  16. private $secure;
  17. private $ready = false;
  18. /**
  19. * @var \CF_Authentication auth
  20. */
  21. private $auth;
  22. /**
  23. * @var \CF_Connection conn
  24. */
  25. private $conn;
  26. /**
  27. * @var \CF_Container rootContainer
  28. */
  29. private $rootContainer;
  30. private static $tempFiles=array();
  31. private $objects=array();
  32. private $containers=array();
  33. const SUBCONTAINER_FILE='.subcontainers';
  34. /**
  35. * translate directory path to container name
  36. * @param string $path
  37. * @return string
  38. */
  39. private function getContainerName($path) {
  40. $path=trim(trim($this->root, '/') . "/".$path, '/.');
  41. return str_replace('/', '\\', $path);
  42. }
  43. /**
  44. * get container by path
  45. * @param string $path
  46. * @return \CF_Container
  47. */
  48. private function getContainer($path) {
  49. if ($path=='' or $path=='/') {
  50. return $this->rootContainer;
  51. }
  52. if (isset($this->containers[$path])) {
  53. return $this->containers[$path];
  54. }
  55. try {
  56. $container=$this->conn->get_container($this->getContainerName($path));
  57. $this->containers[$path]=$container;
  58. return $container;
  59. } catch(\NoSuchContainerException $e) {
  60. return null;
  61. }
  62. }
  63. /**
  64. * create container
  65. * @param string $path
  66. * @return \CF_Container
  67. */
  68. private function createContainer($path) {
  69. if ($path=='' or $path=='/' or $path=='.') {
  70. return $this->conn->create_container($this->getContainerName($path));
  71. }
  72. $parent=dirname($path);
  73. if ($parent=='' or $parent=='/' or $parent=='.') {
  74. $parentContainer=$this->rootContainer;
  75. } else {
  76. if ( ! $this->containerExists($parent)) {
  77. $parentContainer=$this->createContainer($parent);
  78. } else {
  79. $parentContainer=$this->getContainer($parent);
  80. }
  81. }
  82. $this->addSubContainer($parentContainer, basename($path));
  83. return $this->conn->create_container($this->getContainerName($path));
  84. }
  85. /**
  86. * get object by path
  87. * @param string $path
  88. * @return \CF_Object
  89. */
  90. private function getObject($path) {
  91. if (isset($this->objects[$path])) {
  92. return $this->objects[$path];
  93. }
  94. $container=$this->getContainer(dirname($path));
  95. if (is_null($container)) {
  96. return null;
  97. } else {
  98. if ($path=="/" or $path=='') {
  99. return null;
  100. }
  101. try {
  102. $obj=$container->get_object(basename($path));
  103. $this->objects[$path]=$obj;
  104. return $obj;
  105. } catch(\NoSuchObjectException $e) {
  106. return null;
  107. }
  108. }
  109. }
  110. /**
  111. * get the names of all objects in a container
  112. * @param CF_Container
  113. * @return array
  114. */
  115. private function getObjects($container) {
  116. if (is_null($container)) {
  117. return array();
  118. } else {
  119. $files=$container->get_objects();
  120. foreach ($files as &$file) {
  121. $file=$file->name;
  122. }
  123. return $files;
  124. }
  125. }
  126. /**
  127. * create object
  128. * @param string $path
  129. * @return \CF_Object
  130. */
  131. private function createObject($path) {
  132. $container=$this->getContainer(dirname($path));
  133. if ( ! is_null($container)) {
  134. $container=$this->createContainer(dirname($path));
  135. }
  136. return $container->create_object(basename($path));
  137. }
  138. /**
  139. * check if an object exists
  140. * @param string
  141. * @return bool
  142. */
  143. private function objectExists($path) {
  144. return !is_null($this->getObject($path));
  145. }
  146. /**
  147. * check if container for path exists
  148. * @param string $path
  149. * @return bool
  150. */
  151. private function containerExists($path) {
  152. return !is_null($this->getContainer($path));
  153. }
  154. /**
  155. * get the list of emulated sub containers
  156. * @param \CF_Container $container
  157. * @return array
  158. */
  159. private function getSubContainers($container) {
  160. $tmpFile=\OCP\Files::tmpFile();
  161. $obj=$this->getSubContainerFile($container);
  162. try {
  163. $obj->save_to_filename($tmpFile);
  164. } catch(\Exception $e) {
  165. return array();
  166. }
  167. $obj->save_to_filename($tmpFile);
  168. $containers=file($tmpFile);
  169. unlink($tmpFile);
  170. foreach ($containers as &$sub) {
  171. $sub=trim($sub);
  172. }
  173. return $containers;
  174. }
  175. /**
  176. * add an emulated sub container
  177. * @param \CF_Container $container
  178. * @param string $name
  179. * @return bool
  180. */
  181. private function addSubContainer($container, $name) {
  182. if ( ! $name) {
  183. return false;
  184. }
  185. $tmpFile=\OCP\Files::tmpFile();
  186. $obj=$this->getSubContainerFile($container);
  187. try {
  188. $obj->save_to_filename($tmpFile);
  189. $containers=file($tmpFile);
  190. foreach ($containers as &$sub) {
  191. $sub=trim($sub);
  192. }
  193. if(array_search($name, $containers) !== false) {
  194. unlink($tmpFile);
  195. return false;
  196. } else {
  197. $fh=fopen($tmpFile, 'a');
  198. fwrite($fh, $name . "\n");
  199. }
  200. } catch(\Exception $e) {
  201. file_put_contents($tmpFile, $name . "\n");
  202. }
  203. $obj->load_from_filename($tmpFile);
  204. unlink($tmpFile);
  205. return true;
  206. }
  207. /**
  208. * remove an emulated sub container
  209. * @param \CF_Container $container
  210. * @param string $name
  211. * @return bool
  212. */
  213. private function removeSubContainer($container, $name) {
  214. if ( ! $name) {
  215. return false;
  216. }
  217. $tmpFile=\OCP\Files::tmpFile();
  218. $obj=$this->getSubContainerFile($container);
  219. try {
  220. $obj->save_to_filename($tmpFile);
  221. $containers=file($tmpFile);
  222. } catch (\Exception $e) {
  223. return false;
  224. }
  225. foreach ($containers as &$sub) {
  226. $sub=trim($sub);
  227. }
  228. $i=array_search($name, $containers);
  229. if ($i===false) {
  230. unlink($tmpFile);
  231. return false;
  232. } else {
  233. unset($containers[$i]);
  234. file_put_contents($tmpFile, implode("\n", $containers)."\n");
  235. }
  236. $obj->load_from_filename($tmpFile);
  237. unlink($tmpFile);
  238. return true;
  239. }
  240. /**
  241. * ensure a subcontainer file exists and return it's object
  242. * @param \CF_Container $container
  243. * @return \CF_Object
  244. */
  245. private function getSubContainerFile($container) {
  246. try {
  247. return $container->get_object(self::SUBCONTAINER_FILE);
  248. } catch(\NoSuchObjectException $e) {
  249. return $container->create_object(self::SUBCONTAINER_FILE);
  250. }
  251. }
  252. public function __construct($params) {
  253. if (isset($params['token']) && isset($params['host']) && isset($params['user'])) {
  254. $this->token=$params['token'];
  255. $this->host=$params['host'];
  256. $this->user=$params['user'];
  257. $this->root=isset($params['root'])?$params['root']:'/';
  258. if (isset($params['secure'])) {
  259. if (is_string($params['secure'])) {
  260. $this->secure = ($params['secure'] === 'true');
  261. } else {
  262. $this->secure = (bool)$params['secure'];
  263. }
  264. } else {
  265. $this->secure = false;
  266. }
  267. if ( ! $this->root || $this->root[0]!='/') {
  268. $this->root='/'.$this->root;
  269. }
  270. $this->id = 'swift:' . $this->host . ':'.$this->root . ':' . $this->user;
  271. } else {
  272. throw new \Exception();
  273. }
  274. }
  275. private function init(){
  276. if($this->ready) {
  277. return;
  278. }
  279. $this->ready = true;
  280. $this->auth = new \CF_Authentication($this->user, $this->token, null, $this->host);
  281. $this->auth->authenticate();
  282. $this->conn = new \CF_Connection($this->auth);
  283. if ( ! $this->containerExists('/')) {
  284. $this->rootContainer=$this->createContainer('/');
  285. } else {
  286. $this->rootContainer=$this->getContainer('/');
  287. }
  288. }
  289. public function getId(){
  290. return $this->id;
  291. }
  292. public function mkdir($path) {
  293. $this->init();
  294. if ($this->containerExists($path)) {
  295. return false;
  296. } else {
  297. $this->createContainer($path);
  298. return true;
  299. }
  300. }
  301. public function rmdir($path) {
  302. $this->init();
  303. if (!$this->containerExists($path)) {
  304. return false;
  305. } else {
  306. $this->emptyContainer($path);
  307. if ($path!='' and $path!='/') {
  308. $parentContainer=$this->getContainer(dirname($path));
  309. $this->removeSubContainer($parentContainer, basename($path));
  310. }
  311. $this->conn->delete_container($this->getContainerName($path));
  312. unset($this->containers[$path]);
  313. return true;
  314. }
  315. }
  316. private function emptyContainer($path) {
  317. $container=$this->getContainer($path);
  318. if (is_null($container)) {
  319. return;
  320. }
  321. $subContainers=$this->getSubContainers($container);
  322. foreach ($subContainers as $sub) {
  323. if ($sub) {
  324. $this->emptyContainer($path.'/'.$sub);
  325. $this->conn->delete_container($this->getContainerName($path.'/'.$sub));
  326. unset($this->containers[$path.'/'.$sub]);
  327. }
  328. }
  329. $objects=$this->getObjects($container);
  330. foreach ($objects as $object) {
  331. $container->delete_object($object);
  332. unset($this->objects[$path.'/'.$object]);
  333. }
  334. }
  335. public function opendir($path) {
  336. $this->init();
  337. $container=$this->getContainer($path);
  338. $files=$this->getObjects($container);
  339. $i=array_search(self::SUBCONTAINER_FILE, $files);
  340. if ($i!==false) {
  341. unset($files[$i]);
  342. }
  343. $subContainers=$this->getSubContainers($container);
  344. $files=array_merge($files, $subContainers);
  345. $id=$this->getContainerName($path);
  346. \OC\Files\Stream\Dir::register($id, $files);
  347. return opendir('fakedir://'.$id);
  348. }
  349. public function filetype($path) {
  350. $this->init();
  351. if ($this->containerExists($path)) {
  352. return 'dir';
  353. } else {
  354. return 'file';
  355. }
  356. }
  357. public function isReadable($path) {
  358. return true;
  359. }
  360. public function isUpdatable($path) {
  361. return true;
  362. }
  363. public function file_exists($path) {
  364. $this->init();
  365. if ($this->is_dir($path)) {
  366. return true;
  367. } else {
  368. return $this->objectExists($path);
  369. }
  370. }
  371. public function file_get_contents($path) {
  372. $this->init();
  373. $obj=$this->getObject($path);
  374. if (is_null($obj)) {
  375. return false;
  376. }
  377. return $obj->read();
  378. }
  379. public function file_put_contents($path, $content) {
  380. $this->init();
  381. $obj=$this->getObject($path);
  382. if (is_null($obj)) {
  383. $container=$this->getContainer(dirname($path));
  384. if (is_null($container)) {
  385. return false;
  386. }
  387. $obj=$container->create_object(basename($path));
  388. }
  389. $this->resetMTime($obj);
  390. return $obj->write($content);
  391. }
  392. public function unlink($path) {
  393. $this->init();
  394. if ($this->containerExists($path)) {
  395. return $this->rmdir($path);
  396. }
  397. if ($this->objectExists($path)) {
  398. $container=$this->getContainer(dirname($path));
  399. $container->delete_object(basename($path));
  400. unset($this->objects[$path]);
  401. } else {
  402. return false;
  403. }
  404. }
  405. public function fopen($path, $mode) {
  406. $this->init();
  407. switch($mode) {
  408. case 'r':
  409. case 'rb':
  410. $obj=$this->getObject($path);
  411. if (is_null($obj)) {
  412. return false;
  413. }
  414. $fp = fopen('php://temp', 'r+');
  415. $obj->stream($fp);
  416. rewind($fp);
  417. return $fp;
  418. case 'w':
  419. case 'wb':
  420. case 'a':
  421. case 'ab':
  422. case 'r+':
  423. case 'w+':
  424. case 'wb+':
  425. case 'a+':
  426. case 'x':
  427. case 'x+':
  428. case 'c':
  429. case 'c+':
  430. $tmpFile=$this->getTmpFile($path);
  431. \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
  432. self::$tempFiles[$tmpFile]=$path;
  433. return fopen('close://'.$tmpFile, $mode);
  434. }
  435. }
  436. public function writeBack($tmpFile) {
  437. if (isset(self::$tempFiles[$tmpFile])) {
  438. $this->fromTmpFile($tmpFile, self::$tempFiles[$tmpFile]);
  439. unlink($tmpFile);
  440. }
  441. }
  442. public function touch($path, $mtime=null) {
  443. $this->init();
  444. $obj=$this->getObject($path);
  445. if (is_null($obj)) {
  446. return false;
  447. }
  448. if (is_null($mtime)) {
  449. $mtime=time();
  450. }
  451. //emulate setting mtime with metadata
  452. $obj->metadata['Mtime']=$mtime;
  453. $obj->sync_metadata();
  454. }
  455. public function rename($path1, $path2) {
  456. $this->init();
  457. $sourceContainer=$this->getContainer(dirname($path1));
  458. $targetContainer=$this->getContainer(dirname($path2));
  459. $result=$sourceContainer->move_object_to(basename($path1), $targetContainer, basename($path2));
  460. unset($this->objects[$path1]);
  461. if ($result) {
  462. $targetObj=$this->getObject($path2);
  463. $this->resetMTime($targetObj);
  464. }
  465. return $result;
  466. }
  467. public function copy($path1, $path2) {
  468. $this->init();
  469. $sourceContainer=$this->getContainer(dirname($path1));
  470. $targetContainer=$this->getContainer(dirname($path2));
  471. $result=$sourceContainer->copy_object_to(basename($path1), $targetContainer, basename($path2));
  472. if ($result) {
  473. $targetObj=$this->getObject($path2);
  474. $this->resetMTime($targetObj);
  475. }
  476. return $result;
  477. }
  478. public function stat($path) {
  479. $this->init();
  480. $container=$this->getContainer($path);
  481. if ( ! is_null($container)) {
  482. return array(
  483. 'mtime'=>-1,
  484. 'size'=>$container->bytes_used,
  485. 'ctime'=>-1
  486. );
  487. }
  488. $obj=$this->getObject($path);
  489. if (is_null($obj)) {
  490. return false;
  491. }
  492. if (isset($obj->metadata['Mtime']) and $obj->metadata['Mtime']>-1) {
  493. $mtime=$obj->metadata['Mtime'];
  494. } else {
  495. $mtime=strtotime($obj->last_modified);
  496. }
  497. return array(
  498. 'mtime'=>$mtime,
  499. 'size'=>$obj->content_length,
  500. 'ctime'=>-1,
  501. );
  502. }
  503. private function getTmpFile($path) {
  504. $this->init();
  505. $obj=$this->getObject($path);
  506. if ( ! is_null($obj)) {
  507. $tmpFile=\OCP\Files::tmpFile();
  508. $obj->save_to_filename($tmpFile);
  509. return $tmpFile;
  510. } else {
  511. return \OCP\Files::tmpFile();
  512. }
  513. }
  514. private function fromTmpFile($tmpFile, $path) {
  515. $this->init();
  516. $obj=$this->getObject($path);
  517. if (is_null($obj)) {
  518. $obj=$this->createObject($path);
  519. }
  520. $obj->load_from_filename($tmpFile);
  521. $this->resetMTime($obj);
  522. }
  523. /**
  524. * remove custom mtime metadata
  525. * @param \CF_Object $obj
  526. */
  527. private function resetMTime($obj) {
  528. if (isset($obj->metadata['Mtime'])) {
  529. $obj->metadata['Mtime']=-1;
  530. $obj->sync_metadata();
  531. }
  532. }
  533. }