From 565410d8e266b4d0391b10d15339f700dcb1f789 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 7 May 2010 22:50:59 +0200 Subject: [PATCH] use an abstraction for file system functions to allow premisions and multiply storage backends --- files/get_files.php | 2 +- files/open_file.php | 31 +- inc/HTTP/WebDAV/Server.php | 1 - inc/HTTP/WebDAV/Server/Filesystem.php | 204 ++++------ inc/lib_base.php | 3 + inc/lib_files.php | 212 +++-------- inc/lib_filesystem.php | 511 ++++++++++++++++++++++++++ webdav/owncloud.php | 2 + 8 files changed, 656 insertions(+), 310 deletions(-) create mode 100755 inc/lib_filesystem.php diff --git a/files/get_files.php b/files/get_files.php index 21866dbf63..1481bb6f84 100755 --- a/files/get_files.php +++ b/files/get_files.php @@ -42,7 +42,7 @@ function return_bytes($val) { header('Content-type: application/xml'); $dir=isset($_GET['dir'])?$_GET['dir']:''; -$files=OC_FILES::getdirectorycontent(realpath($CONFIG_DATADIRECTORY.'/'.$dir)); +$files=OC_FILES::getdirectorycontent($dir); $dirname=(isset($files[0]))?$files[0]['directory']:''; $dirname=substr($dirname,strrpos($dirname,'/')); $max_upload=min(return_bytes(ini_get('post_max_size')),return_bytes(ini_get('upload_max_filesize'))); diff --git a/files/open_file.php b/files/open_file.php index 933efa51e1..e1b82dcd37 100755 --- a/files/open_file.php +++ b/files/open_file.php @@ -24,40 +24,19 @@ require_once('../inc/lib_base.php'); -function get_mime_type($filename, $mimePath = '../etc') { - $fileext = substr(strrchr($filename, '.'), 1); - if (empty($fileext)) return (false); - $regex = "/^([\w\+\-\.\/]+)\s+(\w+\s)*($fileext\s)/i"; - $lines = file("$mimePath/mime.types"); - foreach($lines as $line) { - if (substr($line, 0, 1) == '#') continue; // skip comments - $line = rtrim($line) . " "; - if (!preg_match($regex, $line, $matches)) continue; // no match to the extension - return ($matches[1]); - } - return (false); // no match at all -} - $file=$_GET['file']; $dir=(isset($_GET['dir']))?$_GET['dir']:''; if(strstr($file,'..') or strstr($dir,'..')){ die(); } -$filename=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$file; -// $ftype='application/octet-stream'; -// $finfo=new finfo(FILEINFO_MIME); -// $fres=@$finfo->file($filename); -// if (is_string($fres) && !empty($fres)) { -// $ftype = $fres; -// } -$ftype=get_mime_type($filename); +$filename=$dir.'/'.$file; +$ftype=OC_FILESYSTEM::getMimeType($filename); ob_end_clean(); -// echo $ftype; -// die(); header('Content-Type: '.$ftype); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); -header('Content-Length: ' . filesize($filename)); -readfile($filename); +header('Content-Length: ' . OC_FILESYSTEM::filesize($filename)); + +OC_FILESYSTEM::readfile($filename); ?> \ No newline at end of file diff --git a/inc/HTTP/WebDAV/Server.php b/inc/HTTP/WebDAV/Server.php index c81b9c90b9..cf8adb31f5 100755 --- a/inc/HTTP/WebDAV/Server.php +++ b/inc/HTTP/WebDAV/Server.php @@ -1255,7 +1255,6 @@ class HTTP_WebDAV_Server return; } } - $options["stream"] = fopen("php://input", "r"); $stat = $this->PUT($options); diff --git a/inc/HTTP/WebDAV/Server/Filesystem.php b/inc/HTTP/WebDAV/Server/Filesystem.php index 013be1c4ef..69ef7b3018 100755 --- a/inc/HTTP/WebDAV/Server/Filesystem.php +++ b/inc/HTTP/WebDAV/Server/Filesystem.php @@ -107,7 +107,7 @@ function PROPFIND(&$options, &$files) { // get absolute fs path to requested resource - $fspath = $this->base . $options["path"]; + $fspath = $options["path"]; // sanity check if (!file_exists($fspath)) { @@ -121,13 +121,13 @@ $files["files"][] = $this->fileinfo($options["path"]); // information for contained resources requested? - if (!empty($options["depth"]) && is_dir($fspath) && is_readable($fspath)) { + if (!empty($options["depth"]) && OC_FILESYSTEM::is_dir($fspath) && OC_FILESYSTEM::is_readable($fspath)) { // make sure path ends with '/' $options["path"] = $this->_slashify($options["path"]); // try to open directory - $handle = @opendir($fspath); + $handle = @OC_FILESYSTEM::opendir($fspath); if ($handle) { // ok, now get all its contents @@ -153,39 +153,39 @@ function fileinfo($path) { // map URI path to filesystem path - $fspath = $this->base . $path; + $fspath =$path; // create result array $info = array(); // TODO remove slash append code when base clase is able to do it itself - $info["path"] = is_dir($fspath) ? $this->_slashify($path) : $path; + $info["path"] = OC_FILESYSTEM::is_dir($fspath) ? $this->_slashify($path) : $path; $info["props"] = array(); // no special beautified displayname here ... $info["props"][] = $this->mkprop("displayname", strtoupper($path)); // creation and modification time - $info["props"][] = $this->mkprop("creationdate", filectime($fspath)); - $info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath)); + $info["props"][] = $this->mkprop("creationdate", OC_FILESYSTEM::filectime($fspath)); + $info["props"][] = $this->mkprop("getlastmodified", OC_FILESYSTEM::filemtime($fspath)); // Microsoft extensions: last access time and 'hidden' status - $info["props"][] = $this->mkprop("lastaccessed", fileatime($fspath)); + $info["props"][] = $this->mkprop("lastaccessed", OC_FILESYSTEM::fileatime($fspath)); $info["props"][] = $this->mkprop("ishidden", ('.' === substr(basename($fspath), 0, 1))); // type and size (caller already made sure that path exists) - if (is_dir($fspath)) { + if ( OC_FILESYSTEM::is_dir($fspath)) { // directory (WebDAV collection) $info["props"][] = $this->mkprop("resourcetype", "collection"); $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory"); } else { // plain file (WebDAV resource) $info["props"][] = $this->mkprop("resourcetype", ""); - if (is_readable($fspath)) { + if ( OC_FILESYSTEM::is_readable($fspath)) { $info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath)); } else { $info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable"); } - $info["props"][] = $this->mkprop("getcontentlength", filesize($fspath)); + $info["props"][] = $this->mkprop("getcontentlength", OC_FILESYSTEM::filesize($fspath)); } // get additional properties from database @@ -251,62 +251,7 @@ */ function _mimetype($fspath) { - if (@is_dir($fspath)) { - // directories are easy - return "httpd/unix-directory"; - } else if (function_exists("mime_content_type")) { - // use mime magic extension if available - $mime_type = mime_content_type($fspath); - } else if ($this->_can_execute("file")) { - // it looks like we have a 'file' command, - // lets see it it does have mime support - $fp = popen("file -i '$fspath' 2>/dev/null", "r"); - $reply = fgets($fp); - pclose($fp); - - // popen will not return an error if the binary was not found - // and find may not have mime support using "-i" - // so we test the format of the returned string - - // the reply begins with the requested filename - if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) { - $reply = substr($reply, strlen($fspath)+2); - // followed by the mime type (maybe including options) - if (preg_match('|^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*|', $reply, $matches)) { - $mime_type = $matches[0]; - } - } - } - - if (empty($mime_type)) { - // Fallback solution: try to guess the type by the file extension - // TODO: add more ... - // TODO: it has been suggested to delegate mimetype detection - // to apache but this has at least three issues: - // - works only with apache - // - needs file to be within the document tree - // - requires apache mod_magic - // TODO: can we use the registry for this on Windows? - // OTOH if the server is Windos the clients are likely to - // be Windows, too, and tend do ignore the Content-Type - // anyway (overriding it with information taken from - // the registry) - // TODO: have a seperate PEAR class for mimetype detection? - switch (strtolower(strrchr(basename($fspath), "."))) { - case ".html": - $mime_type = "text/html"; - break; - case ".gif": - $mime_type = "image/gif"; - break; - case ".jpg": - $mime_type = "image/jpeg"; - break; - default: - $mime_type = "application/octet-stream"; - break; - } - } + return OC_FILESYSTEM::getMimeType($fspath); return $mime_type; } @@ -320,10 +265,10 @@ function HEAD(&$options) { // get absolute fs path to requested resource - $fspath = $this->base . $options["path"]; + $fspath = $options["path"]; // sanity check - if (!file_exists($fspath)) return false; + if (! OC_FILESYSTEM::file_exists($fspath)) return false; // detect resource type $options['mimetype'] = $this->_mimetype($fspath); @@ -332,10 +277,10 @@ // see rfc2518, section 13.7 // some clients seem to treat this as a reverse rule // requiering a Last-Modified header if the getlastmodified header was set - $options['mtime'] = filemtime($fspath); + $options['mtime'] = OC_FILESYSTEM::filemtime($fspath); // detect resource size - $options['size'] = filesize($fspath); + $options['size'] = OC_FILESYSTEM::filesize($fspath); return true; } @@ -348,11 +293,11 @@ */ function GET(&$options) { - // get absolute fs path to requested resource - $fspath = $this->base . $options["path"]; - + // get absolute fs path to requested resource) + $fspath = $options["path"]; + error_log("get '$fspath'"); // is this a collection? - if (is_dir($fspath)) { + if (OC_FILESYSTEM::is_dir($fspath)) { return $this->GetDir($fspath, $options); } @@ -362,7 +307,7 @@ } // no need to check result here, it is handled by the base class - $options['stream'] = fopen($fspath, "r"); + $options['stream'] = OC_FILESYSTEM::fopen($fspath, "r"); return true; } @@ -387,11 +332,11 @@ // fixed width directory column format $format = "%15s %-19s %-s\n"; - if (!is_readable($fspath)) { + if (!OC_FILESYSTEM::is_readable($fspath)) { return false; } - $handle = opendir($fspath); + $handle = OC_FILESYSTEM::opendir($fspath); if (!$handle) { return false; } @@ -432,26 +377,25 @@ */ function PUT(&$options) { - $fspath = $this->base . $options["path"]; + $fspath = $options["path"]; $dir = dirname($fspath); - if (!file_exists($dir) || !is_dir($dir)) { + if (!OC_FILESYSTEM::file_exists($dir) || !OC_FILESYSTEM::is_dir($dir)) { return "409 Conflict"; // TODO right status code for both? } - $options["new"] = ! file_exists($fspath); + $options["new"] = ! OC_FILESYSTEM::file_exists($fspath); - if ($options["new"] && !is_writeable($dir)) { + if ($options["new"] && !OC_FILESYSTEM::is_writeable($dir)) { return "403 Forbidden"; } - if (!$options["new"] && !is_writeable($fspath)) { + if (!$options["new"] && !OC_FILESYSTEM::is_writeable($fspath)) { return "403 Forbidden"; } - if (!$options["new"] && is_dir($fspath)) { + if (!$options["new"] && OC_FILESYSTEM::is_dir($fspath)) { return "403 Forbidden"; } - - $fp = fopen($fspath, "w"); + $fp = OC_FILESYSTEM::fopen($fspath, "w"); return $fp; } @@ -465,19 +409,19 @@ */ function MKCOL($options) { - $path = $this->base .$options["path"]; + $path = $options["path"]; $parent = dirname($path); $name = basename($path); - - if (!file_exists($parent)) { + + if (!OC_FILESYSTEM::file_exists($parent)) { return "409 Conflict"; } - if (!is_dir($parent)) { + if (!OC_FILESYSTEM::is_dir($parent)) { return "403 Forbidden"; } - if ( file_exists($parent."/".$name) ) { + if ( OC_FILESYSTEM::file_exists($parent."/".$name) ) { return "405 Method not allowed"; } @@ -485,7 +429,7 @@ return "415 Unsupported media type"; } - $stat = mkdir($parent."/".$name, 0777); + $stat = OC_FILESYSTEM::mkdir($parent."/".$name, 0777); if (!$stat) { return "403 Forbidden"; } @@ -502,18 +446,20 @@ */ function DELETE($options) { - $path = $this->base . "/" .$options["path"]; + $path =$options["path"]; - if (!file_exists($path)) { + if (!OC_FILESYSTEM::file_exists($path)) { return "404 Not found"; } - if (is_dir($path)) { + if (OC_FILESYSTEM::is_dir($path)) { $query = "DELETE FROM properties WHERE path LIKE '".$this->_slashify($options["path"])."%'"; OC_DB::query($query); - System::rm(array("-rf, $path")); +// System::rm(array("-rf, $path")); + error_log('delTree'); + OC_FILESYSTEM::delTree($path); } else { - unlink($path); + OC_FILESYSTEM::unlink($path); } $query = "DELETE FROM properties WHERE path = '$options[path]'"; OC_DB::query($query); @@ -552,12 +498,12 @@ return "502 bad gateway"; } - $source = $this->base . $options["path"]; - if (!file_exists($source)) { + $source = $options["path"]; + if (!OC_FILESYSTEM::file_exists($source)) { return "404 Not found"; } - if (is_dir($source)) { // resource is a collection + if (OC_FILESYSTEM::is_dir($source)) { // resource is a collection switch ($options["depth"]) { case "infinity": // valid break; @@ -572,24 +518,24 @@ } } - $dest = $this->base . $options["dest"]; + $dest = $options["dest"]; $destdir = dirname($dest); - if (!file_exists($destdir) || !is_dir($destdir)) { + if (!OC_FILESYSTEM::file_exists($destdir) || !OC_FILESYSTEM::is_dir($destdir)) { return "409 Conflict"; } - $new = !file_exists($dest); + $new = !OC_FILESYSTEM::file_exists($dest); $existing_col = false; if (!$new) { - if ($del && is_dir($dest)) { + if ($del && OC_FILESYSTEM::is_dir($dest)) { if (!$options["overwrite"]) { return "412 precondition failed"; } $dest .= basename($source); - if (file_exists($dest)) { + if (OC_FILESYSTEM::file_exists($dest)) { $options["dest"] .= basename($source); } else { $new = true; @@ -610,7 +556,7 @@ } if ($del) { - if (!rename($source, $dest)) { + if (!OC_FILESYSTEM::rename($source, $dest)) { return "500 Internal server error"; } $destpath = $this->_unslashify($options["dest"]); @@ -626,8 +572,8 @@ WHERE path = '".$options["path"]."'"; OC_DB::query($query); } else { - if (is_dir($source)) { - $files = System::find($source); + if (OC_FILESYSTEM::is_dir($source)) { + $files = OC_FILESYSTEM::find($source); $files = array_reverse($files); } else { $files = array($source); @@ -639,26 +585,26 @@ foreach ($files as $file) { - if (is_dir($file)) { + if (OC_FILESYSTEM::is_dir($file)) { $file = $this->_slashify($file); } $destfile = str_replace($source, $dest, $file); - if (is_dir($file)) { - if (!file_exists($destfile)) { - if (!is_writeable(dirname($destfile))) { + if (OC_FILESYSTEM::is_dir($file)) { + if (!OC_FILESYSTEM::file_exists($destfile)) { + if (!OC_FILESYSTEM::is_writeable(dirname($destfile))) { return "403 Forbidden"; } - if (!mkdir($destfile)) { + if (!OC_FILESYSTEM::mkdir($destfile)) { return "409 Conflict"; } - } else if (!is_dir($destfile)) { + } else if (!OC_FILESYSTEM::is_dir($destfile)) { return "409 Conflict"; } } else { - if (!copy($file, $destfile)) { + if (!OC_FILESYSTEM::copy($file, $destfile)) { return "409 Conflict"; } } @@ -712,7 +658,7 @@ function LOCK(&$options) { // get absolute fs path to requested resource - $fspath = $this->base . $options["path"]; + $fspath = $options["path"]; // TODO recursive locks on directories not supported yet // makes litmus test "32. lock_collection" fail @@ -731,7 +677,7 @@ OC_DB::free_result($res); if (is_array($row)) { - $query = "UPDATE locks SET expires = '$options[timeout]', modified = ".time()." $where"; + $query = "UPDATE `locks` SET `expires` = '$options[timeout]', `modified` = ".time()." $where"; OC_DB::query($query); $options['owner'] = $row['owner']; @@ -744,14 +690,14 @@ } } - $query = "INSERT INTO locks - SET token = '$options[locktoken]' - , path = '$options[path]' - , created = ".time()." - , modified = ".time()." - , owner = '$options[owner]' - , expires = '$options[timeout]' - , exclusivelock = " .($options['scope'] === "exclusive" ? "1" : "0") + $query = "INSERT INTO `locks` + SET `token` = '$options[locktoken]' + , `path` = '$options[path]' + , `created` = ".time()." + , `modified` = ".time()." + , `owner` = '$options[owner]' + , `expires` = '$options[timeout]' + , `exclusivelock` = " .($options['scope'] === "exclusive" ? "1" : "0") ; OC_DB::query($query); @@ -783,15 +729,13 @@ function checkLock($path) { $result = false; - - $query = "SELECT owner, token, created, modified, expires, exclusivelock + $query = "SELECT * FROM locks WHERE path = '$path' "; - $res = OC_DB::query($query); - + $res = OC_DB::select($query); if ($res) { - $row = OC_DB::fetch_assoc($res); + $row=$res[0]; OC_DB::free_result($res); if ($row) { diff --git a/inc/lib_base.php b/inc/lib_base.php index a2827787b1..fe8134b68e 100755 --- a/inc/lib_base.php +++ b/inc/lib_base.php @@ -68,6 +68,7 @@ if(isset($CONFIG_HTTPFORCESSL) and $CONFIG_HTTPFORCESSL){ // load core libs oc_require_once('lib_files.php'); +oc_require_once('lib_filesystem.php'); oc_require_once('lib_log.php'); oc_require_once('lib_config.php'); oc_require_once('lib_user.php'); @@ -85,6 +86,8 @@ if(OC_USER::isLoggedIn()){ if(!is_dir($CONFIG_DATADIRECTORY)){ mkdir($CONFIG_DATADIRECTORY); } + $rootStorage=new OC_FILESTORAGE_LOCAL(array('datadir'=>$CONFIG_DATADIRECTORY)); + OC_FILESYSTEM::mount($rootStorage,'/'); } // load plugins diff --git a/inc/lib_files.php b/inc/lib_files.php index 9bd349bfd0..f00f235d74 100755 --- a/inc/lib_files.php +++ b/inc/lib_files.php @@ -28,7 +28,7 @@ * */ class OC_FILES { - + static $tmpFiles=array(); /** * show a web GUI filebrowser * @@ -44,28 +44,32 @@ class OC_FILES { * @param dir $directory */ public static function getdirectorycontent($directory){ + global $CONFIG_DATADIRECTORY; + if(strpos($directory,$CONFIG_DATADIRECTORY)===0){ + $directory=substr($directory,strlen($CONFIG_DATADIRECTORY)); + } $filesfound=true; $content=array(); $dirs=array(); $file=array(); $files=array(); - if (is_dir($directory)) { - if ($dh = opendir($directory)) { + if (OC_FILESYSTEM::is_dir($directory)) { + if ($dh = OC_FILESYSTEM::opendir($directory)) { while (($filename = readdir($dh)) !== false) { - if($filename<>'.' and $filename<>'..'){ - $file=array(); - $filesfound=true; - $file['name']=$filename; - $file['directory']=$directory; - $stat=stat($directory.'/'.$filename); - $file=array_merge($file,$stat); - $file['mime']=OC_FILES::getMimeType($directory .'/'. $filename); - $file['type']=filetype($directory .'/'. $filename); - if($file['type']=='dir'){ - $dirs[$file['name']]=$file; - }else{ - $files[$file['name']]=$file; - } + if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){ + $file=array(); + $filesfound=true; + $file['name']=$filename; + $file['directory']=$directory; + $stat=OC_FILESYSTEM::stat($directory.'/'.$filename); + $file=array_merge($file,$stat); + $file['mime']=OC_FILES::getMimeType($directory .'/'. $filename); + $file['type']=OC_FILESYSTEM::filetype($directory .'/'. $filename); + if($file['type']=='dir'){ + $dirs[$file['name']]=$file; + }else{ + $files[$file['name']]=$file; + } } } closedir($dh); @@ -90,10 +94,6 @@ class OC_FILES { * @param file $file */ public static function get($dir,$files){ - global $CONFIG_DATADIRECTORY; - if(strstr($files,'..') or strstr($dir,'..')){ - die(); - } if(strpos($files,';')){ $files=explode(';',$files); } @@ -104,39 +104,45 @@ class OC_FILES { exit("cannot open <$filename>\n"); } foreach($files as $file){ - $file=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$file; - if(is_file($file)){ - $zip->addFile($file,basename($file)); - }elseif(is_dir($file)){ + $file=$dir.'/'.$file; + if(OC_FILESYSTEM::is_file($file)){ + $tmpFile=OC_FILESYSTEM::toTmpFile($file); + self::$tmpFiles[]=$tmpFile; + $zip->addFile($tmpFile,basename($file)); + }elseif(OC_FILESYSTEM::is_dir($file)){ zipAddDir($file,$zip); } } $zip->close(); - }elseif(is_dir($CONFIG_DATADIRECTORY.'/'.$dir.'/'.$files)){ + }elseif(OC_FILESYSTEM::is_dir($dir.'/'.$files)){ $zip = new ZipArchive(); $filename = sys_get_temp_dir()."/ownCloud.zip"; if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) { exit("cannot open <$filename>\n"); } - $file=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$files; + $file=$dir.'/'.$files; zipAddDir($file,$zip); $zip->close(); }else{ $zip=false; - $filename=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$files; + $filename=$dir.'/'.$files; } - header('Content-Description: File Transfer'); - header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename='.basename($filename)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Pragma: public'); header('Content-Length: ' . filesize($filename)); + if(!$zip){ + $filename=OC_FILESYSTEM::toTmpFile($filename); + } ob_end_clean(); readfile($filename); - if($zip){ - unlink($filename); + unlink($filename); + foreach(self::$tmpFiles as $tmpFile){ + if(file_exists($tmpFile) and is_file($tmpFile)){ + unlink($tmpFile); + } } } @@ -149,11 +155,10 @@ class OC_FILES { * @param file $target */ public static function move($sourceDir,$source,$targetDir,$target){ - global $CONFIG_DATADIRECTORY; - if(OC_USER::isLoggedIn() and strpos($sourceDir,'..')===false and strpos($source,'..')===false and strpos($targetDir,'..')===false and strpos($target,'..')===false){ - $targetFile=$CONFIG_DATADIRECTORY.'/'.$targetDir.'/'.$target; - $sourceFile=$CONFIG_DATADIRECTORY.'/'.$sourceDir.'/'.$source; - rename($sourceFile,$targetFile); + if(OC_USER::isLoggedIn()){ + $targetFile=$targetDir.'/'.$target; + $sourceFile=$sourceDir.'/'.$source; + OC_FILESYSTEM::rename($sourceFile,$targetFile); } } @@ -165,13 +170,12 @@ class OC_FILES { * @param type $type */ public static function newfile($dir,$name,$type){ - global $CONFIG_DATADIRECTORY; - if(OC_USER::isLoggedIn() and strpos($dir,'..')===false and strpos($name,'..')===false){ - $file=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$name; + if(OC_USER::isLoggedIn()){ + $file=$dir.'/'.$name; if($type=='dir'){ - mkdir($file); + OC_FILESYSTEM::mkdir($file); }elseif($type=='file'){ - $fileHandle=fopen($file, 'w') or die("can't open file"); + $fileHandle=OC_FILESYSTEM::fopen($file, 'w') or die("can't open file"); fclose($fileHandle); } } @@ -184,13 +188,12 @@ class OC_FILES { * @param file $name */ public static function delete($dir,$file){ - global $CONFIG_DATADIRECTORY; - if(OC_USER::isLoggedIn() and strpos($dir,'..')===false){ - $file=$CONFIG_DATADIRECTORY.'/'.$dir.'/'.$file; - if(is_file($file)){ - unlink($file); - }elseif(is_dir($file)){ - rmdir($file); + if(OC_USER::isLoggedIn()){ + $file=$dir.'/'.$file; + if(OC_FILESYSTEM::is_file($file)){ + OC_FILESYSTEM::unlink($file); + }elseif(OC_FILESYSTEM::is_dir($file)){ + OC_FILESYSTEM::delTree($file); } } } @@ -198,108 +201,11 @@ class OC_FILES { /** * try to detect the mime type of a file * - * @param string file path + * @param string path * @return string guessed mime type */ - function getMimeType($fspath){ - if (@is_dir($fspath)) { - // directories are easy - return "httpd/unix-directory"; - } else if (function_exists("mime_content_type")) { - // use mime magic extension if available - $mime_type = mime_content_type($fspath); - } else if (OC_FILES::canExecute("file")) { - // it looks like we have a 'file' command, - // lets see it it does have mime support - $fp = popen("file -i '$fspath' 2>/dev/null", "r"); - $reply = fgets($fp); - pclose($fp); - - // popen will not return an error if the binary was not found - // and find may not have mime support using "-i" - // so we test the format of the returned string - - // the reply begins with the requested filename - if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) { - $reply = substr($reply, strlen($fspath)+2); - // followed by the mime type (maybe including options) - if (preg_match('/^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*/', $reply, $matches)) { - $mime_type = $matches[0]; - } - } - } - if (empty($mime_type)) { - // Fallback solution: try to guess the type by the file extension - // TODO: add more ... - switch (strtolower(strrchr(basename($fspath), "."))) { - case ".html": - $mime_type = "text/html"; - break; - case ".txt": - $mime_type = "text/plain"; - break; - case ".css": - $mime_type = "text/css"; - break; - case ".gif": - $mime_type = "image/gif"; - break; - case ".jpg": - $mime_type = "image/jpeg"; - break; - case ".jpg": - $mime_type = "png/jpeg"; - break; - default: - $mime_type = "application/octet-stream"; - break; - } - } - - return $mime_type; - } - - /** - * detect if a given program is found in the search PATH - * - * helper function used by _mimetype() to detect if the - * external 'file' utility is available - * - * @param string program name - * @param string optional search path, defaults to $PATH - * @return bool true if executable program found in path - */ - function canExecute($name, $path = false) - { - // path defaults to PATH from environment if not set - if ($path === false) { - $path = getenv("PATH"); - } - - // check method depends on operating system - if (!strncmp(PHP_OS, "WIN", 3)) { - // on Windows an appropriate COM or EXE file needs to exist - $exts = array(".exe", ".com"); - $check_fn = "file_exists"; - } else { - // anywhere else we look for an executable file of that name - $exts = array(""); - $check_fn = "is_executable"; - } - - // now check the directories in the path for the program - foreach (explode(PATH_SEPARATOR, $path) as $dir) { - // skip invalid path entries - if (!file_exists($dir)) continue; - if (!is_dir($dir)) continue; - - // and now look for the file - foreach ($exts as $ext) { - if ($check_fn("$dir/$name".$ext)) return true; - } - } - - return false; + function getMimeType($path){ + return OC_FILESYSTEM::getMimeType($path); } } @@ -312,9 +218,11 @@ function zipAddDir($dir,$zip,$internalDir=''){ foreach($files as $file){ $filename=$file['name']; $file=$dir.'/'.$filename; - if(is_file($file)){ - $zip->addFile($file,$internalDir.$filename); - }elseif(is_dir($file)){ + if(OC_FILESYSTEM::is_file($file)){ + $tmpFile=OC_FILESYSTEM::toTmpFile($file); + OC_FILES::$tmpFiles[]=$tmpFile; + $zip->addFile($tmpFile,$internalDir.$filename); + }elseif(OC_FILESYSTEM::is_dir($file)){ zipAddDir($file,$zip,$internalDir); } } diff --git a/inc/lib_filesystem.php b/inc/lib_filesystem.php new file mode 100755 index 0000000000..cdfd0f1103 --- /dev/null +++ b/inc/lib_filesystem.php @@ -0,0 +1,511 @@ +. +* +*/ + + +/** + * Class for abstraction of filesystem functions + * This class won't call any filesystem functions for itself but but will pass them to the correct OC_FILESTORAGE object + * this class should also handle all the file premission related stuff + */ +class OC_FILESYSTEM{ + static private $storages=array(); + /** + * check if the current users has the right premissions to read a file + * @param string path + * @return bool + */ + static private function canRead(){ + return true;//dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there + } + /** + * check if the current users has the right premissions to write a file + * @param string path + * @return bool + */ + static private function canWrite(){ + return true;//dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there + } + + /** + * mount an OC_FILESTORAGE in our virtual filesystem + * @param OC_FILESTORAGE storage + * @param string mountpoint + */ + static public function mount($storage,$mountpoint){ + if(substr($mountpoint,0,1)!=='/'){ + $mountpoint='/'.$mountpoint; + } + self::$storages[$mountpoint]=$storage; + } + + /** + * get the storage object for a path + * @param string path + * @return OC_FILESTORAGE + */ + static private function getStorage($path){ + $mountpoint=self::getMountPoint($path); + if($mountpoint){ + return self::$storages[$mountpoint]; + } + } + + /** + * get the mountpoint of the storage object for a path + * @param string path + * @return string + */ + static private function getMountPoint($path){ + if(!$path){ + $path='/'; + } + if(substr($path,0,1)!=='/'){ + $path='/'.$path; + } + $foundMountPoint=''; + foreach(self::$storages as $mountpoint=>$storage){ + if($mountpoint==$path){ + return $mountpoint; + } + if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){ + $foundMountPoint=$mountpoint; + } + } + return $foundMountPoint; + } + + static public function mkdir($path){ + $parent=substr($path,0,strrpos($path,'/')); + if(self::canWrite($parent) and $storage=self::getStorage($path)){ + return $storage->mkdir(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function rmdir($path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->rmdir(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function opendir($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->opendir(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function is_dir($path){ + if($path=='/'){ + return true; + } + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->is_dir(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function is_file($path){ + if($path=='/'){ + return false; + } + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->is_file(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function stat($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->stat(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function filetype($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->filetype(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function filesize($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->filesize(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function readfile($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->readfile(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function is_readable($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->is_readable(substr($path,strlen(self::getMountPoint($path)))); + } + return false; + } + static public function is_writeable($path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->is_writeable(substr($path,strlen(self::getMountPoint($path)))); + } + return false; + } + static public function file_exists($path){ + if($path=='/'){ + return true; + } + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->file_exists(substr($path,strlen(self::getMountPoint($path)))); + } + return false; + } + static public function filectime($path){ + if($storage=self::getStorage($path)){ + return $storage->filectime(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function filemtime($path){ + if($storage=self::getStorage($path)){ + return $storage->filemtime(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function fileatime($path){ + if($storage=self::getStorage($path)){ + return $storage->fileatime(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function file_get_contents($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->file_get_contents(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function file_put_contents($path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->file_put_contents(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function unlink($path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->unlink(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function rename($path1,$path2){ + if(self::canWrite($path1) and self::canWrite($path2)){ + $mp1=self::getMountPoint($path1); + $mp2=self::getMountPoint($path2); + if($mp1==$mp2){ + if($storage=self::getStorage($path1)){ + return $storage->rename(substr($path1,strlen($mp1)),substr($path2,strlen($mp2))); + } + }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){ + $tmpFile=$storage1->toTmpFile(substr($path1,strlen($mp1))); + $result=$storage2->fromTmpFile($tmpFile,substr($path2,strlen($mp2))); + $storage1->unlink(substr($path1,strlen($mp1))); + return $result; + } + } + } + static public function copy($path1,$path2){ + if(self::canRead($path1) and self::canWrite($path2)){ + $mp1=self::getMountPoint($path1); + $mp2=self::getMountPoint($path2); + if($mp1==$mp2){ + if($storage=self::getStorage($path1)){ + return $storage->copy(substr($path1,strlen($mp1)),substr($path2,strlen($mp2))); + } + }elseif($storage1=self::getStorage($path1) and $storage2=self::getStorage($path2)){ + $tmpFile=$storage1->toTmpFile(substr($path1,strlen($mp1))); + return $storage2->fromTmpFile($tmpFile,substr($path2,strlen($mp2))); + } + } + } + static public function fopen($path,$mode){ + $allowed=((strpos($path,'r')===false and strpos($path,'r+')!==false and self::canRead) or self::canWrite($path)); + if($allowed){ + if($storage=self::getStorage($path)){ + return $storage->fopen(substr($path,strlen(self::getMountPoint($path))),$mode); + } + } + } + static public function toTmpFile($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->toTmpFile(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function fromTmpFile($tmpFile,$path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->fromTmpFile($tmpFile,substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function getMimeType($path){ + if(self::canRead($path) and $storage=self::getStorage($path)){ + return $storage->getMimeType(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function delTree($path){ + if(self::canWrite($path) and $storage=self::getStorage($path)){ + return $storage->delTree(substr($path,strlen(self::getMountPoint($path)))); + } + } + static public function find($path){ + if($storage=self::getStorage($path)){ + return $storage->find(substr($path,strlen(self::getMountPoint($path)))); + } + } +} + +/** + * Privde a common interface to all different storage options + */ +interface OC_FILESTORAGE{ + public function __construct($parameters); + public function mkdir($path); + public function rmdir($path); + public function opendir($path); + public function is_dir($path); + public function is_file($path); + public function stat($path); + public function filetype($path); + public function filesize($path); + public function is_readable($path); + public function is_writeable($path); + public function file_exists($path); + public function readfile($path); + public function filectime($path); + public function filemtime($path); + public function fileatime($path); + public function file_get_contents($path); + public function file_put_contents($path); + public function unlink($path); + public function rename($path1,$path2); + public function copy($path1,$path2); + public function fopen($path,$mode); + public function toTmpFile($path);//copy the file to a temporary file, used for cross-storage file actions + public function fromTmpFile($tmpPath,$path);//copy a file from a temporary file, used for cross-storage file actions + public function getMimeType($path); + public function delTree($path); + public function find($path); +} + +/** + * for local filestore, we only have to map the paths + */ +class OC_FILESTORAGE_LOCAL implements OC_FILESTORAGE{ + private $datadir; + public function __construct($arguments){ + $this->datadir=$arguments['datadir']; + if(substr($this->datadir,-1)!=='/'){ + $this->datadir.='/'; + } + } + public function mkdir($path){ + return mkdir($this->datadir.$path); + } + public function rmdir($path){ + return rmdir($this->datadir.$path); + } + public function opendir($path){ + return opendir($this->datadir.$path); + } + public function is_dir($path){ + return is_dir($this->datadir.$path); + } + public function is_file($path){ + return is_file($this->datadir.$path); + } + public function stat($path){ + return stat($this->datadir.$path); + } + public function filetype($path){ + return filetype($this->datadir.$path); + } + public function filesize($path){ + return filesize($this->datadir.$path); + } + public function is_readable($path){ + return is_readable($this->datadir.$path); + } + public function is_writeable($path){ + return is_writeable($this->datadir.$path); + } + public function file_exists($path){ + return file_exists($this->datadir.$path); + } + public function readfile($path){ + return readfile($this->datadir.$path); + } + public function filectime($path){ + return filectime($this->datadir.$path); + } + public function filemtime($path){ + return filemtime($this->datadir.$path); + } + public function fileatime($path){ + return fileatime($this->datadir.$path); + } + public function file_get_contents($path){ + return file_get_contents($this->datadir.$path); + } + public function file_put_contents($path){ + return file_put_contents($this->datadir.$path); + } + public function unlink($path){ + return unlink($this->datadir.$path); + } + public function rename($path1,$path2){ + return rename($this->datadir.$path1,$this->datadir.$path2); + } + public function copy($path1,$path2){ + return copy($this->datadir.$path1,$this->datadir.$path2); + } + public function fopen($path,$mode){ + return fopen($this->datadir.$path,$mode); + } + + public function getMimeType($fspath){ + if (@is_dir($this->datadir.$fspath)) { + // directories are easy + return "httpd/unix-directory"; + } else if (function_exists("mime_content_type")) { + // use mime magic extension if available + $mime_type = mime_content_type($this->datadir.$fspath); + } else if (self::canExecute("file")) { + // it looks like we have a 'file' command, + // lets see it it does have mime support + $fp = popen("file -i '$fspath' 2>/dev/null", "r"); + $reply = fgets($fp); + pclose($fp); + + // popen will not return an error if the binary was not found + // and find may not have mime support using "-i" + // so we test the format of the returned string + + // the reply begins with the requested filename + if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) { + $reply = substr($reply, strlen($fspath)+2); + // followed by the mime type (maybe including options) + if (preg_match('/^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*/', $reply, $matches)) { + $mime_type = $matches[0]; + } + } + } + if (empty($mime_type)) { + // Fallback solution: try to guess the type by the file extension + // TODO: add more ... + switch (strtolower(strrchr(basename($fspath), "."))) { + case ".html": + $mime_type = "text/html"; + break; + case ".txt": + $mime_type = "text/plain"; + break; + case ".css": + $mime_type = "text/css"; + break; + case ".gif": + $mime_type = "image/gif"; + break; + case ".jpg": + $mime_type = "image/jpeg"; + break; + case ".jpeg": + $mime_type = "image/jpeg"; + break; + case ".png": + $mime_type = "image/png"; + break; + default: + $mime_type = "application/octet-stream"; + break; + } + } + + return $mime_type; + } + + /** + * detect if a given program is found in the search PATH + * + * helper function used by _mimetype() to detect if the + * external 'file' utility is available + * + * @param string program name + * @param string optional search path, defaults to $PATH + * @return bool true if executable program found in path + */ + private function canExecute($name, $path = false) + { + // path defaults to PATH from environment if not set + if ($path === false) { + $path = getenv("PATH"); + } + + // check method depends on operating system + if (!strncmp(PHP_OS, "WIN", 3)) { + // on Windows an appropriate COM or EXE file needs to exist + $exts = array(".exe", ".com"); + $check_fn = "file_exists"; + } else { + // anywhere else we look for an executable file of that name + $exts = array(""); + $check_fn = "is_executable"; + } + + // now check the directories in the path for the program + foreach (explode(PATH_SEPARATOR, $path) as $dir) { + // skip invalid path entries + if (!file_exists($dir)) continue; + if (!is_dir($dir)) continue; + + // and now look for the file + foreach ($exts as $ext) { + if ($check_fn("$dir/$name".$ext)) return true; + } + } + + return false; + } + + public function toTmpFile($path){ + $tmpFolder=sys_get_temp_dir(); + $filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.'))); + copy($this->datadir.$path,$filename); + return $filename; + } + + public function fromTmpFile($tmpFile,$path){ + copy($tmpFile,$this->datadir.$path); + unlink($tmpFile); + } + + public function delTree($dir) { + $dirRelative=$dir; + $dir=$this->datadir.$dir; + if (!file_exists($dir)) return true; + if (!is_dir($dir) || is_link($dir)) return unlink($dir); + foreach (scandir($dir) as $item) { + if ($item == '.' || $item == '..') continue; + if(is_file($dir.'/'.$item)){ + unlink($dir.'/'.$item); + }elseif(is_dir($dir.'/'.$item)){ + if (!$this->delTree($dirRelative. "/" . $item)){ + return false; + }; + } + } + return rmdir($dir); + } + + public function find($path){ + return System::find($this->datadir.$path); + } +} +?> diff --git a/webdav/owncloud.php b/webdav/owncloud.php index 289105a4f1..76a3adf9ae 100755 --- a/webdav/owncloud.php +++ b/webdav/owncloud.php @@ -43,6 +43,8 @@ if(OC_USER::login($user,$passwd)){ if(!is_dir($CONFIG_DATADIRECTORY)){ mkdir($CONFIG_DATADIRECTORY); } + $rootStorage=new OC_FILESTORAGE_LOCAL(array('datadir'=>$CONFIG_DATADIRECTORY)); + OC_FILESYSTEM::mount($rootStorage,'/'); $server = new HTTP_WebDAV_Server_Filesystem(); $server->db_name = $CONFIG_DBNAME; $server->ServeRequest($CONFIG_DATADIRECTORY);