From b6ed61eab2fce2de3653b376bef0eeddd431562a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 15:17:39 +0200 Subject: [PATCH 001/117] pass paramters to file proxies by reference so they can be modified --- lib/fileproxy.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 235fc8bf284..7e0722b960e 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -88,11 +88,11 @@ class OC_FileProxy{ $operation='pre'.$operation; foreach($proxies as $proxy){ if($filepath2){ - if(!$proxy->$operation($filepath,$filepath2)){ + if(!$proxy->$operation(&$filepath,&$filepath2)){ return false; } }else{ - if(!$proxy->$operation($filepath)){ + if(!$proxy->$operation(&$filepath)){ return false; } } From abc749feeb14c49d483135f879195b70ee89654f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 17:01:41 +0200 Subject: [PATCH 002/117] make documentation reflect reality a bit better --- lib/fileproxy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 7e0722b960e..1fb22bd1139 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -34,7 +34,7 @@ * A post-proxy recieves 2 arguments, the filepath and the result of the operation. * The return calue of the post-proxy will be used as the new result of the operation * The operations that have a post-proxy are - * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search + * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, fopen, free_space and search */ class OC_FileProxy{ From 3d67cd51c2f42029435343004b3ebe608bcba375 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 17:02:11 +0200 Subject: [PATCH 003/117] encryption proxy wip --- apps/files_encryption/appinfo/app.php | 11 ++ apps/files_encryption/lib/cryptstream.php | 121 ++++++++++++++++++++++ apps/files_encryption/lib/proxy.php | 70 +++++++++++++ lib/crypt.php | 39 +++++-- lib/fileproxy.php | 8 +- lib/filestorage/local.php | 2 +- lib/filesystemview.php | 2 +- 7 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 apps/files_encryption/appinfo/app.php create mode 100644 apps/files_encryption/lib/cryptstream.php create mode 100644 apps/files_encryption/lib/proxy.php diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php new file mode 100644 index 00000000000..82d2544dd14 --- /dev/null +++ b/apps/files_encryption/appinfo/app.php @@ -0,0 +1,11 @@ +. + * + */ + +/** + * transparently encrypted filestream + */ + +class OC_CryptStream{ + private $source; + + public function stream_open($path, $mode, $options, &$opened_path){ + $path=str_replace('crypt://','',$path); + $this->source=OC_FileSystem::fopen($path.'.enc',$mode); + if(!is_resource($this->source)){ + OC_Log::write('files_encryption','failed to open '.$path.'.enc',OC_Log::ERROR); + } + return is_resource($this->source); + } + + public function stream_seek($offset, $whence=SEEK_SET){ + fseek($this->source,$offset,$whence); + } + + public function stream_tell(){ + return ftell($this->source); + } + + public function stream_read($count){ + $pos=0; + $currentPos=ftell($this->source); + $offset=$currentPos%8192; + fseek($this->source,-$offset,SEEK_CUR); + $result=''; + while($count>$pos){ + $data=fread($this->source,8192); + $pos+=8192; + $result.=OC_Crypt::decrypt($data); + } + return substr($result,$offset,$count); + } + + public function stream_write($data){ + $length=strlen($data); + $written=0; + $currentPos=ftell($this->source); + if($currentPos%8192!=0){ + //make sure we always start on a block start + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $block=OC_Crypt::decrypt($encryptedBlock); + $data=substr($block,0,$currentPos%8192).$data; + } + while(strlen($data)>0){ + if(strlen($data)<8192){ + //fetch the current data in that block and append it to the input so we always write entire blocks + $oldPos=ftell($this->source); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,$oldPos); + $block=OC_Crypt::decrypt($encryptedBlock); + $data.=substr($block,strlen($data)); + } + $encrypted=OC_Crypt::encrypt(substr($data,0,8192)); + fwrite($this->source,$encrypted); + $data=substr($data,8192); + } + return $length; + } + + public function stream_set_option($option,$arg1,$arg2){ + switch($option){ + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source,$arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source,$arg1,$arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source,$arg1,$arg2); + } + } + + public function stream_stat(){ + return fstat($this->source); + } + + public function stream_lock($mode){ + flock($this->source,$mode); + } + + public function stream_flush(){ + return fflush($this->source); + } + + public function stream_eof(){ + return feof($this->source); + } + + public function stream_close(){ + return fclose($this->source); + } +} \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php new file mode 100644 index 00000000000..f7a991a344b --- /dev/null +++ b/apps/files_encryption/lib/proxy.php @@ -0,0 +1,70 @@ +. +* +*/ + +/** + * transparent encryption + */ + +class OC_FileProxy_Encryption extends OC_FileProxy{ + public function preFile_put_contents($path,&$data){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','file put contents',OC_Log::DEBUG); + if (is_resource($data)) { + $newData=''; + while(!feof($data)){ + $block=fread($data,8192); + $newData.=OC_Crypt::encrypt($block); + } + $data=$newData; + }else{ + $data=OC_Crypt::blockEncrypt($data); + } + } + } + + public function postFile_get_contents($path,$data){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','file get contents',OC_Log::DEBUG); + return OC_Crypt::blockDecrypt($data); + } + } + + public function postFopen($path,&$result){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','fopen',OC_Log::DEBUG); + fclose($result); + $result=fopen('crypt://'.substr($path,0,-4));//remove the .enc extention so we don't catch the fopen request made by cryptstream + } + } + + public function preReadFile($path){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','readline',OC_Log::DEBUG); + $stream=fopen('crypt://'.substr($path,0,-4)); + while(!feof($stream)){ + print(fread($stream,8192)); + } + return false;//cancel the original request + } + } +} diff --git a/lib/crypt.php b/lib/crypt.php index 60020679480..3e6fa05b85d 100644 --- a/lib/crypt.php +++ b/lib/crypt.php @@ -113,14 +113,13 @@ class OC_Crypt { return($bf->encrypt($contents)); } - - /** - * @brief encryption of a file - * @param $filename - * @param $key the encryption key - * - * This function encrypts a file - */ + /** + * @brief encryption of a file + * @param $filename + * @param $key the encryption key + * + * This function encrypts a file + */ public static function encryptfile( $filename, $key) { $handleread = fopen($filename, "rb"); if($handleread<>FALSE) { @@ -158,6 +157,30 @@ class OC_Crypt { } fclose($handleread); } + + /** + * encrypt data in 8192b sized blocks + */ + public static function blockEncrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::encrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } + + /** + * decrypt data in 8192b sized blocks + */ + public static function blockDecrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::decrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 1fb22bd1139..796fd95cb38 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -83,16 +83,16 @@ class OC_FileProxy{ return $proxies; } - public static function runPreProxies($operation,$filepath,$filepath2=null){ + public static function runPreProxies($operation,&$filepath,&$filepath2=null){ $proxies=self::getProxies($operation,false); $operation='pre'.$operation; foreach($proxies as $proxy){ - if($filepath2){ - if(!$proxy->$operation(&$filepath,&$filepath2)){ + if(!is_null($filepath2)){ + if($proxy->$operation($filepath,$filepath2)===false){ return false; } }else{ - if(!$proxy->$operation(&$filepath)){ + if($proxy->$operation($filepath)===false){ return false; } } diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index dcb516a3afb..ee4b267bcd4 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -74,7 +74,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ public function file_get_contents($path){ return file_get_contents($this->datadir.$path); } - public function file_put_contents($path,$data){ + public function file_put_contents($path,$data=null){ if($return=file_put_contents($this->datadir.$path,$data)){ } } diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 91c6cd17720..a78f3f652ad 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -302,7 +302,7 @@ class OC_FilesystemView { } } if($run){ - if($extraParam){ + if(!is_null($extraParam)){ $result=$storage->$operation($interalPath,$extraParam); }else{ $result=$storage->$operation($interalPath); From 82394f9527817673f3ecbf7e5fd1d4857f0f3fe1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Oct 2011 14:10:51 +0200 Subject: [PATCH 004/117] add option to dissable fileproxies --- lib/fileproxy.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 796fd95cb38..46fc2f49c50 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -39,6 +39,7 @@ class OC_FileProxy{ private static $proxies=array(); + public static $enabled=true; /** * check if this proxy implments a specific proxy operation @@ -84,6 +85,9 @@ class OC_FileProxy{ } public static function runPreProxies($operation,&$filepath,&$filepath2=null){ + if(!self::$enabled){ + return true; + } $proxies=self::getProxies($operation,false); $operation='pre'.$operation; foreach($proxies as $proxy){ @@ -101,6 +105,9 @@ class OC_FileProxy{ } public static function runPostProxies($operation,$path,$result){ + if(!self::$enabled){ + return $result; + } $proxies=self::getProxies($operation,true); $operation='post'.$operation; foreach($proxies as $proxy){ From e2b49541760dabbdfce11bcc8063d31139b6caa3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Oct 2011 14:11:15 +0200 Subject: [PATCH 005/117] simple file encryption wip --- apps/files_encryption/lib/cryptstream.php | 7 +++++-- apps/files_encryption/lib/proxy.php | 17 +++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index e4544313f63..7fbfeaa7a86 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -29,9 +29,12 @@ class OC_CryptStream{ public function stream_open($path, $mode, $options, &$opened_path){ $path=str_replace('crypt://','',$path); - $this->source=OC_FileSystem::fopen($path.'.enc',$mode); + OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); + OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file + $this->source=OC_FileSystem::fopen($path,$mode); + OC_FileProxy::$enabled=true; if(!is_resource($this->source)){ - OC_Log::write('files_encryption','failed to open '.$path.'.enc',OC_Log::ERROR); + OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); } return is_resource($this->source); } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index f7a991a344b..053ac786c33 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -28,7 +28,6 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','file put contents',OC_Log::DEBUG); if (is_resource($data)) { $newData=''; while(!feof($data)){ @@ -44,27 +43,33 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postFile_get_contents($path,$data){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','file get contents',OC_Log::DEBUG); return OC_Crypt::blockDecrypt($data); } } public function postFopen($path,&$result){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','fopen',OC_Log::DEBUG); + $meta=stream_get_meta_data($result); fclose($result); - $result=fopen('crypt://'.substr($path,0,-4));//remove the .enc extention so we don't catch the fopen request made by cryptstream + OC_log::write('file_encryption','mode: '.$meta['mode']); + $result=fopen('crypt://'.$path,$meta['mode']); } } public function preReadFile($path){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','readline',OC_Log::DEBUG); - $stream=fopen('crypt://'.substr($path,0,-4)); + $stream=fopen('crypt://'.$path,'r'); while(!feof($stream)){ print(fread($stream,8192)); } return false;//cancel the original request } } + + public function postGetMimeType($path,$result){ + if(substr($path,-4)=='.enc'){ + return 'text/plain'; + } + return $result; + } } From e9af2185625f5012067cd5fe2ee04cee9828f11b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 20:49:32 +0100 Subject: [PATCH 006/117] use streams instead of temporary files for cross-storage copy and rename --- lib/filesystemview.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a78f3f652ad..27552d25f2b 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -179,9 +179,13 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + while (!feof($source)){ + fwrite($target,fread($source,8192)); + } + $storage1=$this->getStorage($path1); $storage1->unlink($this->getInternalPath($path1)); } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2)); @@ -207,9 +211,14 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + if($target and $source){ + while (!feof($source)){ + fwrite($target,fread($source,8192)); + } + } } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2)); if(!$exists){ From fd4b30ac6f81193cac1e558cc115802717aa88c1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 20:50:35 +0100 Subject: [PATCH 007/117] no post hooks for fopen --- lib/filesystemview.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 27552d25f2b..a7003e84f19 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -317,9 +317,11 @@ class OC_FilesystemView { $result=$storage->$operation($interalPath); } $result=OC_FileProxy::runPostProxies($operation,$path,$result); - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + if($operation!='fopen'){//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + } } } return $result; From e53e7990c404e3ff2a1b7abad1e4c8ad4f89ee2a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 22 Nov 2011 01:48:08 +0100 Subject: [PATCH 008/117] improve get_temp_dir() implementation --- lib/base.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/base.php b/lib/base.php index 880645ff79d..5b6ed9bcb1f 100644 --- a/lib/base.php +++ b/lib/base.php @@ -271,6 +271,8 @@ if(!function_exists('get_temp_dir')) { unlink($temp); return dirname($temp); } + if( $temp=sys_get_temp_dir()) return $temp; + return null; } } From f1cbb9effc7e0672dd9dd6fa810aba36c5749898 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 24 Nov 2011 01:44:54 +0100 Subject: [PATCH 009/117] initial integration of encryption --- apps/files_encryption/appinfo/info.xml | 11 +++ {lib => apps/files_encryption/lib}/crypt.php | 89 +++++++++++-------- apps/files_encryption/lib/cryptstream.php | 49 +++++++++-- apps/files_encryption/lib/proxy.php | 91 ++++++++++++++++---- lib/user.php | 3 +- 5 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 apps/files_encryption/appinfo/info.xml rename {lib => apps/files_encryption/lib}/crypt.php (68%) diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml new file mode 100644 index 00000000000..a8db06aa3db --- /dev/null +++ b/apps/files_encryption/appinfo/info.xml @@ -0,0 +1,11 @@ + + + files_encryption + Encryption + Server side encryption of files + 0.1 + AGPL + Robin Appelman + 3 + + diff --git a/lib/crypt.php b/apps/files_encryption/lib/crypt.php similarity index 68% rename from lib/crypt.php rename to apps/files_encryption/lib/crypt.php index 3e6fa05b85d..4e7739b1d06 100644 --- a/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -37,20 +37,42 @@ require_once('Crypt_Blowfish/Blowfish.php'); * This class is for crypting and decrypting */ class OC_Crypt { + static private $bf = null; - static $encription_extension='.encrypted'; + public static function loginListener($params){ + self::init($params['uid'],$params['password']); + } public static function init($login,$password) { - $_SESSION['user_password'] = $password; // save the password as passcode for the encryption if(OC_User::isLoggedIn()){ - // does key exist? - if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){ - OC_Crypt::createkey($_SESSION['user_password']); + $view=new OC_FilesystemView('/'.$login); + if(!$view->file_exists('/encryption.key')){// does key exist? + OC_Crypt::createkey($password); } + $key=$view->file_get_contents('/encryption.key'); + $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); } } - + /** + * get the blowfish encryption handeler for a key + * @param string $key (optional) + * + * if the key is left out, the default handeler will be used + */ + public static function getBlowfish($key=''){ + if($key){ + return new Crypt_Blowfish($key); + }else{ + if(!isset($_SESSION['enckey'])){ + return false; + } + if(!self::$bf){ + self::$bf=new Crypt_Blowfish($_SESSION['enckey']); + } + return self::$bf; + } + } public static function createkey($passcode) { if(OC_User::isLoggedIn()){ @@ -61,57 +83,58 @@ class OC_Crypt { $enckey=OC_Crypt::encrypt($key,$passcode); // Write the file - $username=OC_USER::getUser(); - @file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey ); + $username=OC_USER::getUser(); + OC_FileProxy::$enabled=false; + $view=new OC_FilesystemView('/'.$username); + $view->file_put_contents('/encryption.key',$enckey); + OC_FileProxy::$enabled=true; } } - public static function changekeypasscode( $newpasscode) { + public static function changekeypasscode($oldPassword, $newPassword) { if(OC_User::isLoggedIn()){ - $username=OC_USER::getUser(); + $username=OC_USER::getUser(); + $view=new OC_FilesystemView('/'.$username); // read old key - $key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key'); + $key=$view->file_get_contents('/encryption.key'); // decrypt key with old passcode - $key=OC_Crypt::decrypt($key, $_SESSION['user_password']); + $key=OC_Crypt::decrypt($key, $oldPassword); // encrypt again with new passcode - $key=OC_Crypt::encrypt($key,$newpassword); + $key=OC_Crypt::encrypt($key, $newPassword); // store the new key - file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key ); - - $_SESSION['user_password']=$newpasscode; + $view->file_put_contents('/encryption.key', $key ); } } /** * @brief encrypts an content * @param $content the cleartext message you want to encrypt - * @param $key the encryption key + * @param $key the encryption key (optional) * @returns encrypted content * * This function encrypts an content */ - public static function encrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); + public static function encrypt( $content, $key='') { + $bf = self::getBlowfish($key); return($bf->encrypt($content)); } - - /** - * @brief decryption of an content - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key - * @returns cleartext content - * - * This function decrypts an content - */ - public static function decrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); + /** + * @brief decryption of an content + * @param $content the cleartext message you want to decrypt + * @param $key the encryption key (optional) + * @returns cleartext content + * + * This function decrypts an content + */ + public static function decrypt( $content, $key='') { + $bf = self::getBlowfish($key); return($bf->encrypt($contents)); - } + } /** * @brief encryption of a file @@ -181,8 +204,4 @@ class OC_Crypt { } return $result; } - - - - } diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 7fbfeaa7a86..00dda7352b3 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -22,19 +22,35 @@ /** * transparently encrypted filestream + * + * you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream) + * and then fopen('crypt://streams/foo'); */ class OC_CryptStream{ + public static $sourceStreams=array(); private $source; + private $path; + private $readBuffer;//for streams that dont support seeking + private $meta=array();//header/meta for source stream public function stream_open($path, $mode, $options, &$opened_path){ $path=str_replace('crypt://','',$path); - OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); - OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file - $this->source=OC_FileSystem::fopen($path,$mode); - OC_FileProxy::$enabled=true; - if(!is_resource($this->source)){ - OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); + if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){ + $this->source=self::$sourceStreams[basename($path)]['stream']; + $this->path=self::$sourceStreams[basename($path)]['path']; + }else{ + $this->path=$path; + OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); + OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file + $this->source=OC_FileSystem::fopen($path,$mode); + OC_FileProxy::$enabled=true; + if(!is_resource($this->source)){ + OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); + } + } + if(is_resource($this->source)){ + $this->meta=stream_get_meta_data($this->source); } return is_resource($this->source); } @@ -51,14 +67,26 @@ class OC_CryptStream{ $pos=0; $currentPos=ftell($this->source); $offset=$currentPos%8192; - fseek($this->source,-$offset,SEEK_CUR); $result=''; + if($offset>0){ + if($this->meta['seekable']){ + fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used + }else{ + $pos=strlen($this->readBuffer); + $result=$this->readBuffer; + } + } while($count>$pos){ $data=fread($this->source,8192); $pos+=8192; - $result.=OC_Crypt::decrypt($data); + if(strlen($data)){ + $result.=OC_Crypt::decrypt($data); + } } - return substr($result,$offset,$count); + if(!$this->meta['seekable']){ + $this->readBuffer=substr($result,$count); + } + return substr($result,0,$count); } public function stream_write($data){ @@ -119,6 +147,9 @@ class OC_CryptStream{ } public function stream_close(){ + if(OC_FileCache::inCache($this->path)){ + OC_FileCache::put($this->path,array('encrypted'=>true)); + } return fclose($this->source); } } \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 053ac786c33..173aea785ec 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -26,38 +26,98 @@ */ class OC_FileProxy_Encryption extends OC_FileProxy{ + private static $blackList=null; //mimetypes blacklisted from encryption + private static $metaData=array(); //metadata cache + + /** + * check if a file should be encrypted during write + * @param string $path + * @return bool + */ + private static function shouldEncrypt($path){ + if(is_null(self::$blackList)){ + self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); + } + if(isset(self::$metaData[$path])){ + $metadata=self::$metaData[$path]; + }else{ + $metadata=OC_FileCache::get($path); + self::$metaData[$path]=$metadata; + } + if($metadata['encrypted']){ + return true; + } + $extention=substr($path,strrpos($path,'.')+1); + if(array_search($extention,self::$blackList)===false){ + return true; + } + } + + /** + * check if a file is encrypted + * @param string $path + * @return bool + */ + private static function isEncrypted($path){ + if(isset(self::$metaData[$path])){ + $metadata=self::$metaData[$path]; + }else{ + $metadata=OC_FileCache::get($path); + self::$metaData[$path]=$metadata; + } + return (bool)$metadata['encrypted']; + } + public function preFile_put_contents($path,&$data){ - if(substr($path,-4)=='.enc'){ + if(self::shouldEncrypt($path)){ + $exists=OC_Filesystem::file_exists($path); + $target=fopen('crypt://'.$path,'w'); if (is_resource($data)) { - $newData=''; while(!feof($data)){ - $block=fread($data,8192); - $newData.=OC_Crypt::encrypt($block); + fwrite($target,fread($data,8192)); } - $data=$newData; }else{ - $data=OC_Crypt::blockEncrypt($data); + fwrite($target,$data); } + //fake the normal hooks + OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path)); + OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path)); + return false; } } public function postFile_get_contents($path,$data){ - if(substr($path,-4)=='.enc'){ - return OC_Crypt::blockDecrypt($data); + if(self::isEncrypted($path)){ + $data=OC_Crypt::blockDecrypt($data); } + return $data; } public function postFopen($path,&$result){ - if(substr($path,-4)=='.enc'){ - $meta=stream_get_meta_data($result); + if(!$result){ + return $result; + } + $meta=stream_get_meta_data($result); + if(self::isEncrypted($path)){ fclose($result); - OC_log::write('file_encryption','mode: '.$meta['mode']); + $result=fopen('crypt://'.$path,$meta['mode']); + }elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){ + if(OC_Filesystem::file_exists($path)){ + //first encrypt the target file so we don't end up with a half encrypted file + OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG); + if($result){ + fclose($result); + } + $tmpFile=OC_Filesystem::toTmpFile($path); + OC_Filesystem::fromTmpFile($tmpFile,$path); + } $result=fopen('crypt://'.$path,$meta['mode']); } + return $result; } public function preReadFile($path){ - if(substr($path,-4)=='.enc'){ + if(self::isEncrypted($path)){ $stream=fopen('crypt://'.$path,'r'); while(!feof($stream)){ print(fread($stream,8192)); @@ -65,11 +125,4 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ return false;//cancel the original request } } - - public function postGetMimeType($path,$result){ - if(substr($path,-4)=='.enc'){ - return 'text/plain'; - } - return $result; - } } diff --git a/lib/user.php b/lib/user.php index 34f44f572e0..aa828de52f5 100644 --- a/lib/user.php +++ b/lib/user.php @@ -195,8 +195,8 @@ class OC_User { if( $run ){ $uid=self::checkPassword( $uid, $password ); if($uid){ - OC_Crypt::init($uid,$password); return self::setUserId($uid); + OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); } } return false; @@ -209,7 +209,6 @@ class OC_User { */ public static function setUserId($uid) { $_SESSION['user_id'] = $uid; - OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid )); return true; } From 501678f981cf8e320d11cf1a780aefeae1537050 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 21:45:41 +0100 Subject: [PATCH 010/117] always mount the root filesystem, sometimes we need the filesystem when not logged in --- lib/util.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util.php b/lib/util.php index 1b1e29b6749..3329789de92 100644 --- a/lib/util.php +++ b/lib/util.php @@ -35,9 +35,9 @@ class OC_Util { $user = OC_User::getUser(); } + //first set up the local "root" storage + OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem - //first set up the local "root" storage - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; if( !is_dir( OC::$CONFIG_DATADIRECTORY )){ From b3a974d8bbcd88602a54cf32cd472802583ab4aa Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 21:49:22 +0100 Subject: [PATCH 011/117] only trigger hooks for the default filesystem view --- apps/files_encryption/lib/crypt.php | 41 ++++++++++++++--------------- lib/filesystemview.php | 22 +++++++++------- lib/user.php | 3 ++- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 4e7739b1d06..0a593b98c4b 100644 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -44,19 +44,20 @@ class OC_Crypt { } public static function init($login,$password) { - if(OC_User::isLoggedIn()){ - $view=new OC_FilesystemView('/'.$login); - if(!$view->file_exists('/encryption.key')){// does key exist? - OC_Crypt::createkey($password); - } - $key=$view->file_get_contents('/encryption.key'); - $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); + $view=new OC_FilesystemView('/'.$login); + OC_FileProxy::$enabled=false; + if(!$view->file_exists('/encryption.key')){// does key exist? + OC_Crypt::createkey($login,$password); } + $key=$view->file_get_contents('/encryption.key'); + OC_FileProxy::$enabled=true; + $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); } /** * get the blowfish encryption handeler for a key * @param string $key (optional) + * @return Crypt_Blowfish * * if the key is left out, the default handeler will be used */ @@ -74,21 +75,19 @@ class OC_Crypt { } } - public static function createkey($passcode) { - if(OC_User::isLoggedIn()){ - // generate a random key - $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); + public static function createkey($username,$passcode) { + // generate a random key + $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); - // encrypt the key with the passcode of the user - $enckey=OC_Crypt::encrypt($key,$passcode); + // encrypt the key with the passcode of the user + $enckey=OC_Crypt::encrypt($key,$passcode); - // Write the file - $username=OC_USER::getUser(); - OC_FileProxy::$enabled=false; - $view=new OC_FilesystemView('/'.$username); - $view->file_put_contents('/encryption.key',$enckey); - OC_FileProxy::$enabled=true; - } + // Write the file + $proxyEnabled=OC_FileProxy::$enabled; + OC_FileProxy::$enabled=false; + $view=new OC_FilesystemView('/'.$username); + $view->file_put_contents('/encryption.key',$enckey); + OC_FileProxy::$enabled=$proxyEnabled; } public static function changekeypasscode($oldPassword, $newPassword) { @@ -133,7 +132,7 @@ class OC_Crypt { */ public static function decrypt( $content, $key='') { $bf = self::getBlowfish($key); - return($bf->encrypt($contents)); + return($bf->decrypt($content)); } /** diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a7003e84f19..0ce803be2b1 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -303,11 +303,13 @@ class OC_FilesystemView { if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ $interalPath=$this->getInternalPath($path); $run=true; - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - }else{ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); + }else{ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + } } } if($run){ @@ -317,10 +319,12 @@ class OC_FilesystemView { $result=$storage->$operation($interalPath); } $result=OC_FileProxy::runPostProxies($operation,$path,$result); - if($operation!='fopen'){//no post hooks for fopen, the file stream is still open - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + if($operation!='fopen'){//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + } } } } diff --git a/lib/user.php b/lib/user.php index aa828de52f5..826215b2289 100644 --- a/lib/user.php +++ b/lib/user.php @@ -195,8 +195,9 @@ class OC_User { if( $run ){ $uid=self::checkPassword( $uid, $password ); if($uid){ - return self::setUserId($uid); + self::setUserId($uid); OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); + return true; } } return false; From 6658f510986aff0e41fee37319a1b0eefa98a4d0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 23:49:22 +0100 Subject: [PATCH 012/117] provide early file system when using webdav --- lib/connector/sabre/auth.php | 1 + lib/util.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php index 1e87c7cee08..8964ef7d0de 100644 --- a/lib/connector/sabre/auth.php +++ b/lib/connector/sabre/auth.php @@ -23,6 +23,7 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { * @return bool */ protected function validateUserPass($username, $password){ + OC_Util::setUpFS();//login hooks may need early access to the filesystem if(OC_User::login($username,$password)){ OC_Util::setUpFS(); return true; diff --git a/lib/util.php b/lib/util.php index 3329789de92..18a5db3e4ee 100644 --- a/lib/util.php +++ b/lib/util.php @@ -8,6 +8,7 @@ class OC_Util { public static $scripts=array(); public static $styles=array(); public static $headers=array(); + private static $rootMounted=false; private static $fsSetup=false; // Can be set up @@ -36,7 +37,10 @@ class OC_Util { } //first set up the local "root" storage - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); + if(!self::$rootMounted){ + OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); + self::$rootMounted=true; + } if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; From 1cffeefa069075054f9c2f676b84ddc02b56232c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 11 Feb 2012 15:48:31 +0100 Subject: [PATCH 013/117] move implementation of from/toTmpFile from the file storage to the filesystem --- lib/filestorage.php | 2 -- lib/filestorage/local.php | 22 ---------------------- lib/filesystemview.php | 34 +++++++++++++++------------------- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/lib/filestorage.php b/lib/filestorage.php index 4523144f6f4..d420427225b 100644 --- a/lib/filestorage.php +++ b/lib/filestorage.php @@ -45,8 +45,6 @@ class OC_Filestorage{ 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 hash($type,$path,$raw){} public function free_space($path){} diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index ee4b267bcd4..de1f83e3e14 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -171,28 +171,6 @@ class OC_Filestorage_Local extends OC_Filestorage{ } } - public function toTmpFile($path){ - $tmpFolder=get_temp_dir(); - $filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.'))); - $fileStats = stat($this->datadir.$path); - if(copy($this->datadir.$path,$filename)){ - touch($filename, $fileStats['mtime'], $fileStats['atime']); - return $filename; - }else{ - return false; - } - } - - public function fromTmpFile($tmpFile,$path){ - $fileStats = stat($tmpFile); - if(rename($tmpFile,$this->datadir.$path)){ - touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']); - return true; - }else{ - return false; - } - } - private function delTree($dir) { $dirRelative=$dir; $dir=$this->datadir.$dir; diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 0ce803be2b1..592fd972a78 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -254,29 +254,25 @@ class OC_FilesystemView { return $this->basicOperation('fopen',$path,$hooks,$mode); } public function toTmpFile($path){ - if(OC_FileProxy::runPreProxies('toTmpFile',$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, array( OC_Filesystem::signal_param_path => $path)); - return $storage->toTmpFile($this->getInternalPath($path)); + if(OC_Filesystem::isValidPath($path)){ + $source=$this->fopen($path,'r'); + $tmpFile=tempnam(get_temp_dir(),'OC_TMP_').substr($path,strrpos($path,'.')); + if($source){ + return file_put_contents($tmpFile,$source); + } } } public function fromTmpFile($tmpFile,$path){ - if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - $run=true; - $exists=$this->file_exists($path); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - } - if($run){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - } - if($run){ - $result=$storage->fromTmpFile($tmpFile,$this->getInternalPath($path)); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path)); - } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); - return $result; + if(OC_Filesystem::isValidPath($path)){ + $source=fopen($tmpFile,'r'); + if($source){ + $this->file_put_contents($path,$source); + unlink($tmpFile); + return true; + }else{ } + }else{ + error_log('invalid path'); } } From 95459d068e47b83ed13348da3e1d03d062209e41 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 11 Feb 2012 16:06:34 +0100 Subject: [PATCH 014/117] non existing files can never be updated --- lib/filecache.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/filecache.php b/lib/filecache.php index 6ae2f8253db..5d299ff24ee 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -515,6 +515,9 @@ class OC_FileCache{ } $view=new OC_FilesystemView($root); } + if(!$view->file_exists($path)){ + return false; + } $mtime=$view->filemtime($path); $isDir=$view->is_dir($path); $path=$root.$path; From 6a8364c3ffeb9d6227f141ae77ba317a1afbfdf6 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 12 Feb 2012 15:56:32 +0100 Subject: [PATCH 015/117] rework the way file_put_contents is handeled --- apps/files_encryption/lib/cryptstream.php | 1 + apps/files_encryption/lib/proxy.php | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 00dda7352b3..97e0846187d 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -90,6 +90,7 @@ class OC_CryptStream{ } public function stream_write($data){ + error_log('write to '. $this->path); $length=strlen($data); $written=0; $currentPos=ftell($this->source); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 173aea785ec..be6ffa4f44d 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -70,19 +70,13 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(self::shouldEncrypt($path)){ - $exists=OC_Filesystem::file_exists($path); - $target=fopen('crypt://'.$path,'w'); if (is_resource($data)) { - while(!feof($data)){ - fwrite($target,fread($data,8192)); - } + $id=md5($path); + OC_CryptStream::$sourceStreams[$id]=array('path'=>$path,'stream'=>$data); + $data=fopen('crypt://streams/'.$id,'r'); }else{ - fwrite($target,$data); + $data=OC_Crypt::blockEncrypt($data); } - //fake the normal hooks - OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path)); - OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path)); - return false; } } From c121a1a1e7d9ae554005b8438d8ad999cdacc601 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 16:23:00 +0100 Subject: [PATCH 016/117] implement file_put_contents with stream data using fopen --- apps/files_encryption/lib/proxy.php | 6 +----- lib/filesystemview.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index be6ffa4f44d..7974d68d487 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -70,11 +70,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(self::shouldEncrypt($path)){ - if (is_resource($data)) { - $id=md5($path); - OC_CryptStream::$sourceStreams[$id]=array('path'=>$path,'stream'=>$data); - $data=fopen('crypt://streams/'.$id,'r'); - }else{ + if (!is_resource($data)) {//stream put contents should have been converter to fopen $data=OC_Crypt::blockEncrypt($data); } } diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 592fd972a78..58d5b3af715 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -163,7 +163,21 @@ class OC_FilesystemView { return $this->basicOperation('file_get_contents',$path,array('read')); } public function file_put_contents($path,$data){ - return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier + $target=$this->fopen($path,'w'); + if($target){ + while(!feof($data)){ + fwrite($target,fread($data,8192)); + } + fclose($target); + fclose($data); + return true; + }else{ + return false; + } + }else{ + return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + } } public function unlink($path){ return $this->basicOperation('unlink',$path,array('delete')); From 325858e9e239b726a207ac150404863df509935c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 20:19:48 +0100 Subject: [PATCH 017/117] add stream wrapper for in-memory files and dont use global variables for the fakedir stream wrapper --- apps/files_sharing/sharedstorage.php | 3 +- lib/base.php | 2 +- lib/fakedirstream.php | 45 ------ lib/streamwrappers.php | 223 +++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 48 deletions(-) delete mode 100644 lib/fakedirstream.php create mode 100644 lib/streamwrappers.php diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index cb641e68a84..07653fc11b5 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -79,7 +79,6 @@ class OC_Filestorage_Shared extends OC_Filestorage { if ($path == "" || $path == "/") { $path = $this->datadir.$path; $sharedItems = OC_Share::getItemsInFolder($path); - global $FAKEDIRS; $files = array(); foreach ($sharedItems as $item) { // If item is in the root of the shared storage provider and the item exists add it to the fakedirs @@ -87,7 +86,7 @@ class OC_Filestorage_Shared extends OC_Filestorage { $files[] = basename($item['target']); } } - $FAKEDIRS['shared'] = $files; + OC_FakeDirStream::$dirs['shared']=$files; return opendir('fakedir://shared'); } else { $source = $this->getSource($path); diff --git a/lib/base.php b/lib/base.php index 5b6ed9bcb1f..94e4907fed2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -279,7 +279,7 @@ if(!function_exists('get_temp_dir')) { OC::init(); -require_once('fakedirstream.php'); +require_once('streamwrappers.php'); diff --git a/lib/fakedirstream.php b/lib/fakedirstream.php deleted file mode 100644 index fa3e64da62c..00000000000 --- a/lib/fakedirstream.php +++ /dev/null @@ -1,45 +0,0 @@ -name=substr($path,strlen('fakedir://')); - $this->index=0; - if(isset($FAKEDIRS[$this->name])){ - $this->data=$FAKEDIRS[$this->name]; - }else{ - $this->data=array(); - } - return true; - } - - public function dir_readdir(){ - if($this->index>=count($this->data)){ - return false; - } - $filename=$this->data[$this->index]; - $this->index++; - return $filename; - } - - public function dir_closedir() { - $this->data=false; - $this->name=''; - return true; - } - - public function dir_rewinddir() { - $this->index=0; - return true; - } -} - -stream_wrapper_register("fakedir", "fakeDirStream"); - diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php new file mode 100644 index 00000000000..1454a99e8d5 --- /dev/null +++ b/lib/streamwrappers.php @@ -0,0 +1,223 @@ +name=substr($path,strlen('fakedir://')); + $this->index=0; + if(!isset(self::$dirs[$this->name])){ + self::$dirs[$this->name]=array(); + } + return true; + } + + public function dir_readdir(){ + if($this->index>=count(self::$dirs[$this->name])){ + return false; + } + $filename=self::$dirs[$this->name][$this->index]; + $this->index++; + return $filename; + } + + public function dir_closedir() { + $this->name=''; + return true; + } + + public function dir_rewinddir() { + $this->index=0; + return true; + } +} + +class OC_StaticStreamWrapper { + public $context; + protected static $data = array(); + + protected $path = ''; + protected $pointer = 0; + protected $writable = false; + + public function stream_close() {} + + public function stream_eof() { + return $this->pointer >= strlen(self::$data[$this->path]); + } + + public function stream_flush() {} + + public function stream_open($path, $mode, $options, &$opened_path) { + switch ($mode[0]) { + case 'r': + if (!isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = isset($mode[1]) && $mode[1] == '+'; + break; + case 'w': + self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + case 'a': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + $this->pointer = strlen(self::$data[$path]); + break; + case 'x': + if (isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = true; + break; + case 'c': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + default: + return false; + } + $opened_path = $this->path; + return true; + } + + public function stream_read($count) { + $bytes = min(strlen(self::$data[$this->path]) - $this->pointer, $count); + $data = substr(self::$data[$this->path], $this->pointer, $bytes); + $this->pointer += $bytes; + return $data; + } + + public function stream_seek($offset, $whence = SEEK_SET) { + $len = strlen(self::$data[$this->path]); + switch ($whence) { + case SEEK_SET: + if ($offset <= $len) { + $this->pointer = $offset; + return true; + } + break; + case SEEK_CUR: + if ($this->pointer + $offset <= $len) { + $this->pointer += $offset; + return true; + } + break; + case SEEK_END: + if ($len + $offset <= $len) { + $this->pointer = $len + $offset; + return true; + } + break; + } + return false; + } + + public function stream_stat() { + $size = strlen(self::$data[$this->path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + + public function stream_tell() { + return $this->pointer; + } + + public function stream_write($data) { + if (!$this->writable) return 0; + $size = strlen($data); + $len = strlen(self::$data[$this->path]); + if ($this->stream_eof()) { + self::$data[$this->path] .= $data; + } else { + self::$data[$this->path] = substr_replace( + self::$data[$this->path], + $data, + $this->pointer + ); + } + $this->pointer += $size; + return $size; + } + + public function unlink($path) { + if (isset(self::$data[$path])) { + unset(self::$data[$path]); + } + return true; + } + + public function url_stat($path) { + if (isset(self::$data[$path])) { + $size = strlen(self::$data[$path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + return false; + } +} + +stream_wrapper_register("fakedir", "OC_FakeDirStream"); +stream_wrapper_register('static', 'OC_StaticStreamWrapper'); From d9c7e4c333f858efaaee35d26ea12733d29bd694 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 21:44:58 +0100 Subject: [PATCH 018/117] proper mimetypes for encrypted files --- apps/files_encryption/lib/proxy.php | 4 ++ lib/filestorage/local.php | 47 ++--------------------- lib/helper.php | 58 +++++++++++++++++++++++++++++ tests/lib/streamwrappers.php | 47 +++++++++++++++++++++++ 4 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 tests/lib/streamwrappers.php diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 7974d68d487..c53d567ad10 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -115,4 +115,8 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ return false;//cancel the original request } } + + public function postGetMimeType($path,$mime){ + return OC_Helper::getMimeType('crypt://'.$path,'w'); + } } diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index de1f83e3e14..6f4f68c503d 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -122,50 +122,9 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $return; } - public function getMimeType($fspath){ - if($this->is_readable($fspath)){ - $mimeType='application/octet-stream'; - if ($mimeType=='application/octet-stream') { - self::$mimetypes = include('mimetypes.fixlist.php'); - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - - } - if (@is_dir($this->datadir.$fspath)) { - // directories are easy - return "httpd/unix-directory"; - } - if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ - $mimeType =strtolower(finfo_file($finfo,$this->datadir.$fspath)); - $mimeType=substr($mimeType,0,strpos($mimeType,';')); - finfo_close($finfo); - } - if ($mimeType=='application/octet-stream' && function_exists("mime_content_type")) { - // use mime magic extension if available - $mimeType = mime_content_type($this->datadir.$fspath); - } - if ($mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { - // it looks like we have a 'file' command, - // lets see it it does have mime support - $fspath=str_replace("'","\'",$fspath); - $fp = popen("file -i -b '{$this->datadir}$fspath' 2>/dev/null", "r"); - $reply = fgets($fp); - pclose($fp); - - //trim the character set from the end of the response - $mimeType=substr($reply,0,strrpos($reply,' ')); - } - if ($mimeType=='application/octet-stream') { - // Fallback solution: (try to guess the type by the file extension - if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ - self::$mimetypes=include('mimetypes.list.php'); - } - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - } - return $mimeType; + public function getMimeType($path){ + if($this->is_readable($path)){ + return OC_Helper::getMimeType($this->datadir.$path); }else{ return false; } diff --git a/lib/helper.php b/lib/helper.php index 2f71bdad2dc..6dea4a6a8cd 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -25,6 +25,8 @@ * Collection of useful functions */ class OC_Helper { + private static $mimetypes=array(); + /** * @brief Creates an url * @param $app app @@ -267,6 +269,62 @@ class OC_Helper { unlink($dir); } } + + /** + * get the mimetype form a local file + * @param string path + * @return string + * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead + */ + static function getMimeType($path){ + $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://'); + $mimeType='application/octet-stream'; + if ($mimeType=='application/octet-stream') { + if(count(self::$mimetypes)>0){ + self::$mimetypes = include('mimetypes.fixlist.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + + } + if (@is_dir($path)) { + // directories are easy + return "httpd/unix-directory"; + } + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ + $info = @strtolower(finfo_file($finfo,$path)); + if($info){ + $mimeType=substr($info,0,strpos($info,';')); + } + finfo_close($finfo); + } + if (!$isWrapped and $mimeType=='application/octet-stream' && function_exists("mime_content_type")) { + // use mime magic extension if available + $mimeType = mime_content_type($path); + } + if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { + // it looks like we have a 'file' command, + // lets see it it does have mime support + $path=str_replace("'","\'",$path); + $fp = popen("file -i -b '$path' 2>/dev/null", "r"); + $reply = fgets($fp); + pclose($fp); + + //trim the character set from the end of the response + $mimeType=substr($reply,0,strrpos($reply,' ')); + } + if ($mimeType=='application/octet-stream') { + // Fallback solution: (try to guess the type by the file extension + if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ + self::$mimetypes=include('mimetypes.list.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + } + return $mimeType; + } /** * @brief Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d. diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php new file mode 100644 index 00000000000..c4784a62976 --- /dev/null +++ b/tests/lib/streamwrappers.php @@ -0,0 +1,47 @@ +. +* +*/ + +class Test_StreamWrappers extends UnitTestCase { + public function testFakeDir(){ + $items=array('foo','bar'); + OC_FakeDirStream::$dirs['test']=$items; + $dh=opendir('fakedir://test'); + $result=array(); + while($file=readdir($dh)){ + $result[]=$file; + $this->assertNotIdentical(false,array_search($file,$items)); + } + $this->assertEqual(count($items),count($result)); + } + + public function testStaticStream(){ + $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $staticFile='static://test'; + $this->assertFalse(file_exists($staticFile)); + file_put_contents($staticFile,file_get_contents($sourceFile)); + $this->assertTrue(file_exists($staticFile)); + $this->assertEqual(file_get_contents($sourceFile),file_get_contents($staticFile)); + unlink($staticFile); + clearstatcache(); + $this->assertFalse(file_exists($staticFile)); + } +} \ No newline at end of file From c20319d37701efb9d32c38dd71880739ca75b33f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 21 Feb 2012 20:48:14 +0100 Subject: [PATCH 019/117] fix incorrect information in the filecache when using encryption --- apps/files_encryption/lib/cryptstream.php | 5 +-- apps/files_encryption/lib/proxy.php | 31 ++++++++++-------- apps/media/lib_media.php | 1 + files/ajax/upload.php | 3 +- lib/filecache.php | 40 ++++++++++++++++++++--- lib/filesystemview.php | 6 +++- 6 files changed, 62 insertions(+), 24 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 97e0846187d..86583096f1d 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -90,7 +90,6 @@ class OC_CryptStream{ } public function stream_write($data){ - error_log('write to '. $this->path); $length=strlen($data); $written=0; $currentPos=ftell($this->source); @@ -148,9 +147,7 @@ class OC_CryptStream{ } public function stream_close(){ - if(OC_FileCache::inCache($this->path)){ - OC_FileCache::put($this->path,array('encrypted'=>true)); - } + OC_FileCache::put($this->path,array('encrypted'=>true)); return fclose($this->source); } } \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index c53d567ad10..ed3907cccfe 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -38,13 +38,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if(is_null(self::$blackList)){ self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); } - if(isset(self::$metaData[$path])){ - $metadata=self::$metaData[$path]; - }else{ - $metadata=OC_FileCache::get($path); - self::$metaData[$path]=$metadata; - } - if($metadata['encrypted']){ + if(self::isEncrypted($path)){ return true; } $extention=substr($path,strrpos($path,'.')+1); @@ -62,7 +56,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if(isset(self::$metaData[$path])){ $metadata=self::$metaData[$path]; }else{ - $metadata=OC_FileCache::get($path); + $metadata=OC_FileCache::getCached($path); self::$metaData[$path]=$metadata; } return (bool)$metadata['encrypted']; @@ -92,14 +86,19 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ fclose($result); $result=fopen('crypt://'.$path,$meta['mode']); }elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){ - if(OC_Filesystem::file_exists($path)){ + if(OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0){ //first encrypt the target file so we don't end up with a half encrypted file OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG); - if($result){ - fclose($result); + $tmp=fopen('php://temp'); + while(!feof($result)){ + $chunk=fread($result,8192); + if($chunk){ + fwrite($tmp,$chunk); + } } - $tmpFile=OC_Filesystem::toTmpFile($path); - OC_Filesystem::fromTmpFile($tmpFile,$path); + fclose($result); + OC_Filesystem::file_put_contents($path,$tmp); + fclose($tmp); } $result=fopen('crypt://'.$path,$meta['mode']); } @@ -117,6 +116,10 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ } public function postGetMimeType($path,$mime){ - return OC_Helper::getMimeType('crypt://'.$path,'w'); + if((!OC_FileCache::inCache($path) and self::shouldEncrypt($path)) or self::isEncrypted($path)){ + return OC_Helper::getMimeType('crypt://'.$path,'w'); + }else{ + return $mime; + } } } diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php index 1bcd0f08c80..a2109c151aa 100644 --- a/apps/media/lib_media.php +++ b/apps/media/lib_media.php @@ -56,6 +56,7 @@ class OC_MEDIA{ */ public static function updateFile($params){ $path=$params['path']; + if(!$path) return; require_once 'lib_scanner.php'; require_once 'lib_collection.php'; //fix a bug where there were multiply '/' in front of the path, it should only be one diff --git a/files/ajax/upload.php b/files/ajax/upload.php index 241edc216ff..034b8914607 100644 --- a/files/ajax/upload.php +++ b/files/ajax/upload.php @@ -48,7 +48,8 @@ if(strpos($dir,'..') === false){ for($i=0;$i<$fileCount;$i++){ $target=stripslashes($dir) . $files['name'][$i]; if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i],$target)){ - $result[]=array( "status" => "success", 'mime'=>OC_Filesystem::getMimeType($target),'size'=>OC_Filesystem::filesize($target),'name'=>$files['name'][$i]); + $meta=OC_FileCache::getCached($target); + $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'],'name'=>$files['name'][$i]); } } OC_JSON::encodedPrint($result); diff --git a/lib/filecache.php b/lib/filecache.php index 5d299ff24ee..83eaffb171a 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -28,6 +28,8 @@ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache */ class OC_FileCache{ + private static $savedData=array(); + /** * get the filesystem info from the cache * @param string path @@ -93,6 +95,14 @@ class OC_FileCache{ self::update($id,$data); return; } + if(isset(self::$savedData[$path])){ + $data=array_merge($data,self::$savedData[$path]); + unset(self::$savedData[$path]); + } + if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it + self::$savedData[$path]=$data; + return; + } if(!isset($data['encrypted'])){ $data['encrypted']=false; } @@ -101,9 +111,8 @@ class OC_FileCache{ } $mimePart=dirname($data['mimetype']); $user=OC_User::getUser(); - $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable) VALUES(?,?,?,?,?,?,?,?,?,?)'); - $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'])); - + $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)'); + $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned'])); } /** @@ -323,7 +332,29 @@ class OC_FileCache{ } self::increaseSize(dirname($fullPath),$size-$cachedSize); } - + + public static function getCached($path,$root=''){ + if(!$root){ + $root=OC_Filesystem::getRoot(); + }else{ + if($root=='/'){ + $root=''; + } + } + $path=$root.$path; + $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?'); + $result=$query->execute(array($path))->fetchRow(); + if(is_array($result)){ + if(isset(self::$savedData[$path])){ + $result=array_merge($result,self::$savedData[$path]); + } + return $result; + }else{ + OC_Log::write('get(): file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return false; + } + } + private static function getCachedSize($path,$root){ if(!$root){ $root=OC_Filesystem::getRoot(); @@ -332,6 +363,7 @@ class OC_FileCache{ $root=''; } } + $path=$root.$path; $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?'); $result=$query->execute(array($path)); if($row=$result->fetchRow()){ diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 58d5b3af715..c4d5ff35142 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -171,6 +171,7 @@ class OC_FilesystemView { } fclose($target); fclose($data); + OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); return true; }else{ return false; @@ -278,6 +279,9 @@ class OC_FilesystemView { } public function fromTmpFile($tmpFile,$path){ if(OC_Filesystem::isValidPath($path)){ + if(!$tmpFile){ + debug_print_backtrace(); + } $source=fopen($tmpFile,'r'); if($source){ $this->file_put_contents($path,$source); @@ -286,7 +290,7 @@ class OC_FilesystemView { }else{ } }else{ - error_log('invalid path'); + return false; } } From ed0c99ef149d63a350c7ffcb87d398b90ed3b0a2 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 22 Feb 2012 15:18:22 +0100 Subject: [PATCH 020/117] make sure we always have the encryption key unlocked --- apps/files_encryption/appinfo/app.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index 82d2544dd14..23f3955aa40 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -9,3 +9,9 @@ OC_FileProxy::register(new OC_FileProxy_Encryption()); OC_Hook::connect('OC_User','post_login','OC_Crypt','loginListener'); stream_wrapper_register('crypt','OC_CryptStream'); + +if(!isset($_SESSION['enckey']) and OC_User::isLoggedIn()){//force the user to re-loggin if the encryption key isn't unlocked (happens when a user is logged in before the encryption app is enabled) + OC_User::logout(); + header("Location: ".OC::$WEBROOT.'/'); + exit(); +} From 6c6b570ff15cfa9da6b1ab8b7e56ef0a82c96086 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 18:04:21 +0000 Subject: [PATCH 021/117] avoid notice in error log from user_webfinger app --- apps/user_webfinger/host-meta.php | 2 +- apps/user_webfinger/webfinger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index ceb15f22da4..dbb4377e8c7 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -10,7 +10,7 @@ echo "<"; ?xml version="1.0" encoding="UTF-8"?> - + diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index d695a833f31..a9537182016 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -22,7 +22,7 @@ if($_GET['q']) { if(substr($userName, 0, 5) == 'acct:') { $userName = substr($userName, 5); } -if($_SERVER['HTTPS']) { +if(isset($_SERVER['HTTPS'])) { $baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; } else { $baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; From 9850820b4276b197433bb64d84ed085cdcd01e0e Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 18:05:52 +0000 Subject: [PATCH 022/117] BearerAuth and multiple tokens support in remoteStorage app --- apps/remoteStorage/BearerAuth.php | 61 ++++++++++++++++++++++++ apps/remoteStorage/WebDAV.php | 1 + apps/remoteStorage/auth.php | 4 +- apps/remoteStorage/lib_remoteStorage.php | 32 +++++++------ apps/remoteStorage/oauth_ro_auth.php | 4 +- 5 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 apps/remoteStorage/BearerAuth.php diff --git a/apps/remoteStorage/BearerAuth.php b/apps/remoteStorage/BearerAuth.php new file mode 100644 index 00000000000..ebcf189dfb9 --- /dev/null +++ b/apps/remoteStorage/BearerAuth.php @@ -0,0 +1,61 @@ +httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + + return array($user,$pass); + + } + + // Most other webservers + $auth = $this->httpRequest->getHeader('Authorization'); + + if (!$auth) return false; + + if (strpos(strtolower($auth),'bearer')!==0) return false; + + return explode(':', base64_decode(substr($auth, 7))); + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); + $this->httpResponse->sendStatus(401); + + } + +} diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/WebDAV.php index e048d19e8f2..06520b4021b 100644 --- a/apps/remoteStorage/WebDAV.php +++ b/apps/remoteStorage/WebDAV.php @@ -33,6 +33,7 @@ require_once('../../lib/base.php'); OC_Util::checkAppEnabled('remoteStorage'); require_once('Sabre/autoload.php'); require_once('lib_remoteStorage.php'); +require_once('BearerAuth.php'); require_once('oauth_ro_auth.php'); ini_set('default_charset', 'UTF-8'); diff --git a/apps/remoteStorage/auth.php b/apps/remoteStorage/auth.php index 85421ba3d88..75e0aac419d 100644 --- a/apps/remoteStorage/auth.php +++ b/apps/remoteStorage/auth.php @@ -68,14 +68,14 @@ if(count($pathParts) == 2 && $pathParts[0] == '') { } else if($k=='redirect_uri'){ $appUrl=$v; } else if($k=='scope'){ - $category=$v; + $categories=$v; } } $currUser = OC_User::getUser(); if($currUser == $ownCloudUser) { if(isset($_POST['allow'])) { //TODO: check if this can be faked by editing the cookie in firebug! - $token=OC_remoteStorage::createCategory($appUrl, $category); + $token=OC_remoteStorage::createCategories($appUrl, $categories); header('Location: '.$_GET['redirect_uri'].'#access_token='.$token.'&token_type=bearer'); } else { echo '
'; diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php index 4f19310904e..4f5c9664509 100644 --- a/apps/remoteStorage/lib_remoteStorage.php +++ b/apps/remoteStorage/lib_remoteStorage.php @@ -2,11 +2,13 @@ class OC_remoteStorage { public static function getValidTokens($ownCloudUser, $category) { - $query=OC_DB::prepare("SELECT token,appUrl FROM *PREFIX*authtoken WHERE user=? AND category=? LIMIT 100"); - $result=$query->execute(array($ownCloudUser,$category)); + $query=OC_DB::prepare("SELECT token,appUrl,category FROM *PREFIX*authtoken WHERE user=? LIMIT 100"); + $result=$query->execute(array($ownCloudUser)); $ret = array(); while($row=$result->fetchRow()){ - $ret[$row['token']]=true; + if(in_array($category, explode(',', $row['category']))) { + $ret[$row['token']]=true; + } } return $ret; } @@ -19,7 +21,7 @@ class OC_remoteStorage { while($row=$result->fetchRow()){ $ret[$row['token']] = array( 'appUrl' => $row['appurl'], - 'category' => $row['category'], + 'categories' => $row['category'], ); } return $ret; @@ -30,21 +32,23 @@ class OC_remoteStorage { $query=OC_DB::prepare("DELETE FROM *PREFIX*authtoken WHERE token=? AND user=?"); $result=$query->execute(array($token,$user)); } - private static function addToken($token, $appUrl, $category){ + private static function addToken($token, $appUrl, $categories){ $user=OC_User::getUser(); $query=OC_DB::prepare("INSERT INTO *PREFIX*authtoken (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); - $result=$query->execute(array($token,$appUrl,$user,$category)); + $result=$query->execute(array($token,$appUrl,$user,$categories)); } - public static function createCategory($appUrl, $category) { + public static function createCategories($appUrl, $categories) { $token=uniqid(); - self::addToken($token, $appUrl, $category); - //TODO: input checking on $category OC_Util::setupFS(OC_User::getUser()); - $scopePathParts = array('remoteStorage', $category); - for($i=0;$i<=count($scopePathParts);$i++){ - $thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i)); - if(!OC_Filesystem::file_exists($thisPath)) { - OC_Filesystem::mkdir($thisPath); + self::addToken($token, $appUrl, $categories); + foreach($categories as $category) { + //TODO: input checking on $category + $scopePathParts = array('remoteStorage', $category); + for($i=0;$i<=count($scopePathParts);$i++){ + $thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i)); + if(!OC_Filesystem::file_exists($thisPath)) { + OC_Filesystem::mkdir($thisPath); + } } } return base64_encode('remoteStorage:'.$token); diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php index 5403fbe20c9..d4a55061492 100644 --- a/apps/remoteStorage/oauth_ro_auth.php +++ b/apps/remoteStorage/oauth_ro_auth.php @@ -34,7 +34,7 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa if(in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS'))) { OC_Util::setUpFS(); return true; - } else if(isset($this->validTokens[$password]) && $this->validTokens[$password] == $username) { + } else if(isset($this->validTokens[$password])) { OC_Util::setUpFS(); return true; } else { @@ -47,7 +47,7 @@ die('not getting in with "'.$username.'"/"'.$password.'"!'); //overwriting this to make it not automatically fail if no auth header is found: public function authenticate(Sabre_DAV_Server $server,$realm) { - $auth = new Sabre_HTTP_BasicAuth(); + $auth = new Sabre_HTTP_BearerAuth(); $auth->setHTTPRequest($server->httpRequest); $auth->setHTTPResponse($server->httpResponse); $auth->setRealm($realm); From 3f87c2cedb271d498f5e198ce90df4948bc032fa Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 19:05:41 +0000 Subject: [PATCH 023/117] correct header Content-Type: application/xrd+xml --- apps/user_webfinger/host-meta.php | 2 +- apps/user_webfinger/webfinger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index dbb4377e8c7..ac577cf9a0c 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -4,7 +4,7 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/host-meta.php') { } else { header('Please-first: activate'); } -header("Content-Type: application/xml+xrd"); +header("Content-Type: application/xrd+xml"); echo "<"; ?> ?xml version="1.0" encoding="UTF-8"?> diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index a9537182016..5c2a24aa070 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -4,7 +4,7 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') { } else { header('Please-first: activate'); } -// header("Content-Type: application/xml+xrd"); +header("Content-Type: application/xrd+xml"); // calculate the documentroot // modified version of the one in lib/base.php that takes the .well-known symlink into account From 8bfc73c188216f5f0cb21a8c2c30b145435d042b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 22 Feb 2012 21:38:15 +0100 Subject: [PATCH 024/117] some multiselect improvements --- core/js/multiselect.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core/js/multiselect.js b/core/js/multiselect.js index 96fc09a0759..541dddf0f70 100644 --- a/core/js/multiselect.js +++ b/core/js/multiselect.js @@ -12,6 +12,11 @@ 'minWidth': 'default;', }; $.extend(settings,options); + $.each(this.children(),function(i,option){ + if($(option).attr('selected') && settings.checked.indexOf($(option).val())==-1){ + settings.checked.push($(option).val()); + } + }); var button=$('
'+settings.title+'â–¾
'); var span=$(''); span.append(button); @@ -46,9 +51,11 @@ }); button.addClass('active'); event.stopPropagation(); - var options=$(this).parent().next().children().map(function(){return $(this).val();}); + var options=$(this).parent().next().children(); var list=$('