filesystem.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2012 Frank Karlitschek frank@owncloud.org
  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. */
  22. /**
  23. * Class for abstraction of filesystem functions
  24. * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object
  25. * this class should also handle all the file permission related stuff
  26. *
  27. * Hooks provided:
  28. * read(path)
  29. * write(path, &run)
  30. * post_write(path)
  31. * create(path, &run) (when a file is created, both create and write will be emited in that order)
  32. * post_create(path)
  33. * delete(path, &run)
  34. * post_delete(path)
  35. * rename(oldpath,newpath, &run)
  36. * post_rename(oldpath,newpath)
  37. * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order)
  38. * post_rename(oldpath,newpath)
  39. *
  40. * the &run parameter can be set to false to prevent the operation from occuring
  41. */
  42. class OC_Filesystem{
  43. static private $storages=array();
  44. static private $mounts=array();
  45. public static $loaded=false;
  46. /**
  47. * @var OC_Filestorage $defaultInstance
  48. */
  49. static private $defaultInstance;
  50. /**
  51. * classname which used for hooks handling
  52. * used as signalclass in OC_Hooks::emit()
  53. */
  54. const CLASSNAME = 'OC_Filesystem';
  55. /**
  56. * signalname emited before file renaming
  57. * @param oldpath
  58. * @param newpath
  59. */
  60. const signal_rename = 'rename';
  61. /**
  62. * signal emited after file renaming
  63. * @param oldpath
  64. * @param newpath
  65. */
  66. const signal_post_rename = 'post_rename';
  67. /**
  68. * signal emited before file/dir creation
  69. * @param path
  70. * @param run changing this flag to false in hook handler will cancel event
  71. */
  72. const signal_create = 'create';
  73. /**
  74. * signal emited after file/dir creation
  75. * @param path
  76. * @param run changing this flag to false in hook handler will cancel event
  77. */
  78. const signal_post_create = 'post_create';
  79. /**
  80. * signal emits before file/dir copy
  81. * @param oldpath
  82. * @param newpath
  83. * @param run changing this flag to false in hook handler will cancel event
  84. */
  85. const signal_copy = 'copy';
  86. /**
  87. * signal emits after file/dir copy
  88. * @param oldpath
  89. * @param newpath
  90. */
  91. const signal_post_copy = 'post_copy';
  92. /**
  93. * signal emits before file/dir save
  94. * @param path
  95. * @param run changing this flag to false in hook handler will cancel event
  96. */
  97. const signal_write = 'write';
  98. /**
  99. * signal emits after file/dir save
  100. * @param path
  101. */
  102. const signal_post_write = 'post_write';
  103. /**
  104. * signal emits when reading file/dir
  105. * @param path
  106. */
  107. const signal_read = 'read';
  108. /**
  109. * signal emits when removing file/dir
  110. * @param path
  111. */
  112. const signal_delete = 'delete';
  113. /**
  114. * parameters definitions for signals
  115. */
  116. const signal_param_path = 'path';
  117. const signal_param_oldpath = 'oldpath';
  118. const signal_param_newpath = 'newpath';
  119. /**
  120. * run - changing this flag to false in hook handler will cancel event
  121. */
  122. const signal_param_run = 'run';
  123. /**
  124. * get the mountpoint of the storage object for a path
  125. ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
  126. *
  127. * @param string path
  128. * @return string
  129. */
  130. static public function getMountPoint($path){
  131. OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path));
  132. if(!$path){
  133. $path='/';
  134. }
  135. if($path[0]!=='/'){
  136. $path='/'.$path;
  137. }
  138. $path=str_replace('//', '/',$path);
  139. $foundMountPoint='';
  140. $mountPoints=array_keys(OC_Filesystem::$mounts);
  141. foreach($mountPoints as $mountpoint){
  142. if($mountpoint==$path){
  143. return $mountpoint;
  144. }
  145. if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){
  146. $foundMountPoint=$mountpoint;
  147. }
  148. }
  149. return $foundMountPoint;
  150. }
  151. /**
  152. * get the part of the path relative to the mountpoint of the storage it's stored in
  153. * @param string path
  154. * @return bool
  155. */
  156. static public function getInternalPath($path){
  157. $mountPoint=self::getMountPoint($path);
  158. $internalPath=substr($path,strlen($mountPoint));
  159. return $internalPath;
  160. }
  161. /**
  162. * get the storage object for a path
  163. * @param string path
  164. * @return OC_Filestorage
  165. */
  166. static public function getStorage($path){
  167. $mountpoint=self::getMountPoint($path);
  168. if($mountpoint){
  169. if(!isset(OC_Filesystem::$storages[$mountpoint])){
  170. $mount=OC_Filesystem::$mounts[$mountpoint];
  171. OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']);
  172. }
  173. return OC_Filesystem::$storages[$mountpoint];
  174. }
  175. }
  176. static public function init($root){
  177. if(self::$defaultInstance){
  178. return false;
  179. }
  180. self::$defaultInstance=new OC_FilesystemView($root);
  181. //load custom mount config
  182. if(is_file(OC::$SERVERROOT.'/config/mount.php')){
  183. $mountConfig=include(OC::$SERVERROOT.'/config/mount.php');
  184. if(isset($mountConfig['global'])){
  185. foreach($mountConfig['global'] as $mountPoint=>$options){
  186. self::mount($options['class'],$options['options'],$mountPoint);
  187. }
  188. }
  189. if(isset($mountConfig['group'])){
  190. foreach($mountConfig['group'] as $group=>$mounts){
  191. if(OC_Group::inGroup(OC_User::getUser(),$group)){
  192. foreach($mounts as $mountPoint=>$options){
  193. $mountPoint=self::setUserVars($mountPoint);
  194. foreach($options as &$option){
  195. $option=self::setUserVars($option);
  196. }
  197. self::mount($options['class'],$options['options'],$mountPoint);
  198. }
  199. }
  200. }
  201. }
  202. if(isset($mountConfig['user'])){
  203. foreach($mountConfig['user'] as $user=>$mounts){
  204. if($user==='all' or strtolower($user)===strtolower(OC_User::getUser())){
  205. foreach($mounts as $mountPoint=>$options){
  206. $mountPoint=self::setUserVars($mountPoint);
  207. foreach($options as &$option){
  208. $option=self::setUserVars($option);
  209. }
  210. self::mount($options['class'],$options['options'],$mountPoint);
  211. }
  212. }
  213. }
  214. }
  215. }
  216. self::$loaded=true;
  217. }
  218. /**
  219. * fill in the correct values for $user, and $password placeholders
  220. * @param string intput
  221. * @return string
  222. */
  223. private static function setUserVars($input){
  224. return str_replace('$user',OC_User::getUser(),$input);
  225. }
  226. /**
  227. * get the default filesystem view
  228. * @return OC_FilesystemView
  229. */
  230. static public function getView(){
  231. return self::$defaultInstance;
  232. }
  233. /**
  234. * tear down the filesystem, removing all storage providers
  235. */
  236. static public function tearDown(){
  237. self::$storages=array();
  238. }
  239. /**
  240. * create a new storage of a specific type
  241. * @param string type
  242. * @param array arguments
  243. * @return OC_Filestorage
  244. */
  245. static private function createStorage($class,$arguments){
  246. if(class_exists($class)){
  247. try {
  248. return new $class($arguments);
  249. } catch (Exception $exception) {
  250. OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR);
  251. return false;
  252. }
  253. }else{
  254. OC_Log::write('core','storage backend '.$class.' not found',OC_Log::ERROR);
  255. return false;
  256. }
  257. }
  258. /**
  259. * change the root to a fake root
  260. * @param string fakeRoot
  261. * @return bool
  262. */
  263. static public function chroot($fakeRoot){
  264. return self::$defaultInstance->chroot($fakeRoot);
  265. }
  266. /**
  267. * @brief get the relative path of the root data directory for the current user
  268. * @return string
  269. *
  270. * Returns path like /admin/files
  271. */
  272. static public function getRoot(){
  273. return self::$defaultInstance->getRoot();
  274. }
  275. /**
  276. * clear all mounts and storage backends
  277. */
  278. public static function clearMounts(){
  279. self::$mounts=array();
  280. self::$storages=array();
  281. }
  282. /**
  283. * mount an OC_Filestorage in our virtual filesystem
  284. * @param OC_Filestorage storage
  285. * @param string mountpoint
  286. */
  287. static public function mount($class,$arguments,$mountpoint){
  288. if($mountpoint[0]!='/'){
  289. $mountpoint='/'.$mountpoint;
  290. }
  291. if(substr($mountpoint,-1)!=='/'){
  292. $mountpoint=$mountpoint.'/';
  293. }
  294. self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments);
  295. }
  296. /**
  297. * return the path to a local version of the file
  298. * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
  299. * @param string path
  300. * @return string
  301. */
  302. static public function getLocalFile($path){
  303. return self::$defaultInstance->getLocalFile($path);
  304. }
  305. /**
  306. * @param string path
  307. * @return string
  308. */
  309. static public function getLocalFolder($path){
  310. return self::$defaultInstance->getLocalFolder($path);
  311. }
  312. /**
  313. * return path to file which reflects one visible in browser
  314. * @param string path
  315. * @return string
  316. */
  317. static public function getLocalPath($path) {
  318. $datadir = OC_User::getHome($user).'/files';
  319. $newpath = $path;
  320. if (strncmp($newpath, $datadir, strlen($datadir)) == 0) {
  321. $newpath = substr($path, strlen($datadir));
  322. }
  323. return $newpath;
  324. }
  325. /**
  326. * check if the requested path is valid
  327. * @param string path
  328. * @return bool
  329. */
  330. static public function isValidPath($path){
  331. if(!$path || $path[0]!=='/'){
  332. $path='/'.$path;
  333. }
  334. if(strstr($path,'/../') || strrchr($path, '/') === '/..' ){
  335. return false;
  336. }
  337. return true;
  338. }
  339. /**
  340. * checks if a file is blacklsited for storage in the filesystem
  341. * Listens to write and rename hooks
  342. * @param array $data from hook
  343. */
  344. static public function isBlacklisted($data){
  345. $blacklist = array('.htaccess');
  346. if (isset($data['path'])) {
  347. $path = $data['path'];
  348. } else if (isset($data['newpath'])) {
  349. $path = $data['newpath'];
  350. }
  351. if (isset($path)) {
  352. $filename = strtolower(basename($path));
  353. if (in_array($filename, $blacklist)) {
  354. $data['run'] = false;
  355. }
  356. }
  357. }
  358. /**
  359. * following functions are equivilent to their php buildin equivilents for arguments/return values.
  360. */
  361. static public function mkdir($path){
  362. return self::$defaultInstance->mkdir($path);
  363. }
  364. static public function rmdir($path){
  365. return self::$defaultInstance->rmdir($path);
  366. }
  367. static public function opendir($path){
  368. return self::$defaultInstance->opendir($path);
  369. }
  370. static public function readdir($path){
  371. return self::$defaultInstance->readdir($path);
  372. }
  373. static public function is_dir($path){
  374. return self::$defaultInstance->is_dir($path);
  375. }
  376. static public function is_file($path){
  377. return self::$defaultInstance->is_file($path);
  378. }
  379. static public function stat($path){
  380. return self::$defaultInstance->stat($path);
  381. }
  382. static public function filetype($path){
  383. return self::$defaultInstance->filetype($path);
  384. }
  385. static public function filesize($path){
  386. return self::$defaultInstance->filesize($path);
  387. }
  388. static public function readfile($path){
  389. return self::$defaultInstance->readfile($path);
  390. }
  391. /**
  392. * @deprecated Replaced by isReadable() as part of CRUDS
  393. */
  394. static public function is_readable($path){
  395. return self::$defaultInstance->is_readable($path);
  396. }
  397. /**
  398. * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS
  399. */
  400. static public function is_writable($path){
  401. return self::$defaultInstance->is_writable($path);
  402. }
  403. static public function isCreatable($path) {
  404. return self::$defaultInstance->isCreatable($path);
  405. }
  406. static public function isReadable($path) {
  407. return self::$defaultInstance->isReadable($path);
  408. }
  409. static public function isUpdatable($path) {
  410. return self::$defaultInstance->isUpdatable($path);
  411. }
  412. static public function isDeletable($path) {
  413. return self::$defaultInstance->isDeletable($path);
  414. }
  415. static public function isSharable($path) {
  416. return self::$defaultInstance->isSharable($path);
  417. }
  418. static public function file_exists($path){
  419. return self::$defaultInstance->file_exists($path);
  420. }
  421. static public function filectime($path){
  422. return self::$defaultInstance->filectime($path);
  423. }
  424. static public function filemtime($path){
  425. return self::$defaultInstance->filemtime($path);
  426. }
  427. static public function touch($path, $mtime=null){
  428. return self::$defaultInstance->touch($path, $mtime);
  429. }
  430. static public function file_get_contents($path){
  431. return self::$defaultInstance->file_get_contents($path);
  432. }
  433. static public function file_put_contents($path,$data){
  434. return self::$defaultInstance->file_put_contents($path,$data);
  435. }
  436. static public function unlink($path){
  437. return self::$defaultInstance->unlink($path);
  438. }
  439. static public function rename($path1,$path2){
  440. return self::$defaultInstance->rename($path1,$path2);
  441. }
  442. static public function copy($path1,$path2){
  443. return self::$defaultInstance->copy($path1,$path2);
  444. }
  445. static public function fopen($path,$mode){
  446. return self::$defaultInstance->fopen($path,$mode);
  447. }
  448. static public function toTmpFile($path){
  449. return self::$defaultInstance->toTmpFile($path);
  450. }
  451. static public function fromTmpFile($tmpFile,$path){
  452. return self::$defaultInstance->fromTmpFile($tmpFile,$path);
  453. }
  454. static public function getMimeType($path){
  455. return self::$defaultInstance->getMimeType($path);
  456. }
  457. static public function hash($type,$path, $raw = false){
  458. return self::$defaultInstance->hash($type,$path, $raw);
  459. }
  460. static public function free_space($path='/'){
  461. return self::$defaultInstance->free_space($path);
  462. }
  463. static public function search($query){
  464. return OC_FileCache::search($query);
  465. }
  466. /**
  467. * check if a file or folder has been updated since $time
  468. * @param int $time
  469. * @return bool
  470. */
  471. static public function hasUpdated($path,$time){
  472. return self::$defaultInstance->hasUpdated($path,$time);
  473. }
  474. static public function removeETagHook($params) {
  475. if (isset($params['path'])) {
  476. $path=$params['path'];
  477. } else {
  478. $path=$params['oldpath'];
  479. }
  480. OC_Connector_Sabre_Node::removeETagPropertyForPath($path);
  481. }
  482. /**
  483. * normalize a path
  484. * @param string path
  485. * @param bool $stripTrailingSlash
  486. * @return string
  487. */
  488. public static function normalizePath($path,$stripTrailingSlash=true){
  489. if($path==''){
  490. return '/';
  491. }
  492. //no windows style slashes
  493. $path=str_replace('\\','/',$path);
  494. //add leading slash
  495. if($path[0]!=='/'){
  496. $path='/'.$path;
  497. }
  498. //remove trainling slash
  499. if($stripTrailingSlash and strlen($path)>1 and substr($path,-1,1)==='/'){
  500. $path=substr($path,0,-1);
  501. }
  502. //remove duplicate slashes
  503. while(strpos($path,'//')!==false){
  504. $path=str_replace('//','/',$path);
  505. }
  506. //normalize unicode if possible
  507. if(class_exists('Normalizer')){
  508. $path=Normalizer::normalize($path);
  509. }
  510. return $path;
  511. }
  512. }
  513. OC_Hook::connect('OC_Filesystem','post_write', 'OC_Filesystem','removeETagHook');
  514. OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook');
  515. OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook');
  516. OC_Util::setupFS();
  517. require_once('filecache.php');