diff --git a/files/upload.php b/files/upload.php index 0aa435cad6..1190b466ea 100755 --- a/files/upload.php +++ b/files/upload.php @@ -26,9 +26,9 @@ require_once('../inc/lib_base.php'); $fileName=$_FILES['file']['name']; $source=$_FILES['file']['tmp_name']; -$target=$CONFIG_DATADIRECTORY.'/'.$_GET['dir'].'/'.$fileName; +$target=$_GET['dir'].'/'.$fileName; if(isset($_SESSION['username']) and $_SESSION['username'] and strpos($_GET['dir'],'..')===false){ - if(move_uploaded_file($source,$target)){ + if(OC_FILESYSTEM::fromTmpFile($source,$target)){ echo 'true'; }else{ echo 'false'; diff --git a/inc/lib_base.php b/inc/lib_base.php index fe8134b68e..c501a28f4a 100755 --- a/inc/lib_base.php +++ b/inc/lib_base.php @@ -47,7 +47,9 @@ if($WEBROOT{0}!=='/'){ // define default config values $CONFIG_INSTALLED=false; $CONFIG_DATADIRECTORY=$SERVERROOT.'/data'; +$CONFIG_BACKUPDIRECTORY=$SERVERROOT.'/backup'; $CONFIG_HTTPFORCESSL=false; +$CONFIG_ENABLEBACKUP=false; $CONFIG_DATEFORMAT='j M Y G:i'; $CONFIG_DBNAME='owncloud'; $CONFIG_DBTYPE='sqlite'; @@ -69,6 +71,8 @@ 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_filestorage.php'); +oc_require_once('lib_fileobserver.php'); oc_require_once('lib_log.php'); oc_require_once('lib_config.php'); oc_require_once('lib_user.php'); @@ -87,6 +91,17 @@ if(OC_USER::isLoggedIn()){ mkdir($CONFIG_DATADIRECTORY); } $rootStorage=new OC_FILESTORAGE_LOCAL(array('datadir'=>$CONFIG_DATADIRECTORY)); + if($CONFIG_ENABLEBACKUP){ + if(!is_dir($CONFIG_BACKUPDIRECTORY)){ + mkdir($CONFIG_BACKUPDIRECTORY); + } + if(!is_dir($CONFIG_BACKUPDIRECTORY.'/'.$_SESSION['username_clean'])){ + mkdir($CONFIG_BACKUPDIRECTORY.'/'.$_SESSION['username_clean']); + } + $backupStorage=new OC_FILESTORAGE_LOCAL(array('datadir'=>$CONFIG_BACKUPDIRECTORY.'/'.$_SESSION['username_clean'])); + $backup=new OC_FILEOBSERVER_BACKUP(array('storage'=>$backupStorage)); + $rootStorage->addObserver($backup); + } OC_FILESYSTEM::mount($rootStorage,'/'); } diff --git a/inc/lib_config.php b/inc/lib_config.php index f5809a32ae..845c416ba2 100755 --- a/inc/lib_config.php +++ b/inc/lib_config.php @@ -160,6 +160,9 @@ class OC_CONFIG{ if($_POST['dbpassword']<>$_POST['dbpassword2'] ) $error.='database passwords are not the same
'; } + if(isset($_POST['enablebackup']) and $_POST['enablebackup']==1){ + if(!isset($_POST['backupdirectory']) or empty($_POST['backupdirectory'])) $error.='backup directory not set
'; + } if(!$FIRSTRUN){ if(!isset($_POST['adminpassword']) or empty($_POST['adminpassword'])){ $_POST['adminpassword']=$CONFIG_ADMINPASSWORD; @@ -226,6 +229,10 @@ class OC_CONFIG{ $config.='$CONFIG_INSTALLED=true;'."\n"; $config.='$CONFIG_DATADIRECTORY=\''.$_POST['datadirectory']."';\n"; if(isset($_POST['forcessl'])) $config.='$CONFIG_HTTPFORCESSL=true'.";\n"; else $config.='$CONFIG_HTTPFORCESSL=false'.";\n"; + if(isset($_POST['enablebackup'])) $config.='$CONFIG_ENABLEBACKUP=true'.";\n"; else $config.='$CONFIG_ENABLEBACKUP=false'.";\n"; + if(isset($_POST['enablebackup']) and $_POST['enablebackup']==1){ + $config.='$CONFIG_BACKUPDIRECTORY=\''.$_POST['backupdirectory']."';\n"; + } $config.='$CONFIG_DATEFORMAT=\''.$_POST['dateformat']."';\n"; $config.='$CONFIG_DBTYPE=\''.$dbtype."';\n"; $config.='$CONFIG_DBNAME=\''.$_POST['dbname']."';\n"; diff --git a/inc/lib_fileobserver.php b/inc/lib_fileobserver.php new file mode 100644 index 0000000000..c9c73faa6d --- /dev/null +++ b/inc/lib_fileobserver.php @@ -0,0 +1,82 @@ +. +*/ + +define('OC_FILEACTION_WRITE',2); +define('OC_FILEACTION_READ',4); +define('OC_FILEACTION_DELETE',8); +define('OC_FILEACTION_CREATE',16); +define('OC_FILEACTION_RENAME',32); + +/** + * base class for file observers + */ +class OC_FILEOBSERVER{ + private $mask; + + public function __construct($arguments){} + + public function __get($name){ + switch($name){ + case 'mask': + return $this->mask; + } + } + + public function notify($path,$action){} +} + +/** + * observer that makes automatic backups + */ +class OC_FILEOBSERVER_BACKUP extends OC_FILEOBSERVER{ + private $storage; + + public function __construct($arguments){ + $this->mask=OC_FILEACTION_WRITE+OC_FILEACTION_DELETE+OC_FILEACTION_CREATE+OC_FILEACTION_RENAME; + $this->storage=$arguments['storage']; + } + + public function notify($path,$action,$storage){ + switch($action){ + case OC_FILEACTION_DELETE: + if($storage->is_dir($path)){ + $this->storage->delTree($path); + }else{ + $this->storage->unlink($path); + } + break; + case OC_FILEACTION_CREATE: + if($storage->is_dir($path)){ + $this->storage->mkdir($path); + break; + } + case OC_FILEACTION_WRITE: + $tmpFile=$storage->toTmpFile($path); + $this->storage->fromTmpFile($tmpFile,$path); + break; + case OC_FILEACTION_RENAME: + list($source,$target)=explode('->',$path); + $this->storage->rename($source,$target); + } + } +} +?> \ No newline at end of file diff --git a/inc/lib_filestorage.php b/inc/lib_filestorage.php new file mode 100644 index 0000000000..c3161b25d3 --- /dev/null +++ b/inc/lib_filestorage.php @@ -0,0 +1,347 @@ +. +*/ + +/** + * Privde a common interface to all different storage options + */ +class OC_FILESTORAGE{ + private $observers=array(); + /** + * add an observer to the list + * @param OC_FILEOBERSER observer + */ + public function addObserver($observer){ + $this->observers[]=$observer; + } + /** + * notify the observers about an action + * @param int action a combination of OC_FILEACTION_WRITE and OC_FILEACTION_READ + * @param string path relative path of the file + */ + public function notifyObservers($path,$action){ + foreach($this->observers as $observer){ + if($observer->mask & $action){ + $observer->notify($path,$action,$this); + } + } + } + + 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 extends OC_FILESTORAGE{ + private $datadir; + public function __construct($arguments){ + $this->datadir=$arguments['datadir']; + if(substr($this->datadir,-1)!=='/'){ + $this->datadir.='/'; + } + } + public function mkdir($path){ + if($return=mkdir($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_CREATE); + } + return $return; + } + public function rmdir($path){ + if($return=rmdir($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_DELETE); + } + return $return; + } + public function opendir($path){ + if($return=opendir($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_READ); + } + return $return; + } + 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){ + if($return=readfile($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_READ); + } + return $return; + } + 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){ + if($return=file_get_contents($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_READ); + } + return $return; + } + public function file_put_contents($path){ + if($return=file_put_contents($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_WRITE); + } + } + public function unlink($path){ + if($return=unlink($this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_DELETE); + } + return $return; + } + public function rename($path1,$path2){ + if($return=rename($this->datadir.$path1,$this->datadir.$path2)){ + $this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME); + } + return $return; + } + public function copy($path1,$path2){ + if($return=copy($this->datadir.$path1,$this->datadir.$path2)){ + $this->notifyObservers($path2,OC_FILEACTION_CREATE); + } + return $return; + } + public function fopen($path,$mode){ + if($return=fopen($this->datadir.$path,$mode)){ + switch($mode){ + case 'r': + $this->notifyObservers($path,OC_FILEACTION_READ); + break; + case 'r+': + case 'w+': + case 'x+': + case 'a+': + $this->notifyObservers($path,OC_FILEACTION_READ | OC_FILEACTION_WRITE); + break; + case 'w': + case 'x': + case 'a': + $this->notifyObservers($path,OC_FILEACTION_WRITE); + break; + } + } + return $return; + } + + 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,'.'))); + if(copy($this->datadir.$path,$filename)){ + $this->notifyObservers($path,OC_FILEACTION_READ); + return $filename; + }else{ + return false; + } + } + + public function fromTmpFile($tmpFile,$path){ + if(rename($tmpFile,$this->datadir.$path)){ + $this->notifyObservers($path,OC_FILEACTION_CREATE); + return true; + }else{ + return false; + } + } + + 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)){ + if(unlink($dir.'/'.$item)){ + $this->notifyObservers($dir.'/'.$item,OC_FILEACTION_DELETE); + } + }elseif(is_dir($dir.'/'.$item)){ + if (!$this->delTree($dirRelative. "/" . $item)){ + return false; + }; + } + } + if($return=rmdir($dir)){ + $this->notifyObservers($dir,OC_FILEACTION_DELETE); + } + return $return; + } + + public function find($path){ + return System::find($this->datadir.$path); + } +} +?> \ No newline at end of file diff --git a/inc/lib_filesystem.php b/inc/lib_filesystem.php index cdfd0f1103..934da6b0e3 100755 --- a/inc/lib_filesystem.php +++ b/inc/lib_filesystem.php @@ -189,6 +189,7 @@ class OC_FILESYSTEM{ } static public function file_put_contents($path){ if(self::canWrite($path) and $storage=self::getStorage($path)){ + $this->notifyObservers($path,OC_FILEACTION_WRITE | OC_FILEACTION_CREATE); return $storage->file_put_contents(substr($path,strlen(self::getMountPoint($path)))); } } @@ -261,251 +262,4 @@ class OC_FILESYSTEM{ } } } - -/** - * 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/inc/templates/adminform.php b/inc/templates/adminform.php index 7dfdcc65bf..32b4f9ec41 100755 --- a/inc/templates/adminform.php +++ b/inc/templates/adminform.php @@ -1,5 +1,7 @@ ?> data directory: force ssl:> +enable automatic backup:> +backup directory: date format: database type: