only zip backend is implemented atmremotes/origin/stable4
parent
c609b30841
commit
042878a5a9
@ -0,0 +1,14 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php'; |
||||
foreach(array('ZIP') as $type){ |
||||
OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php'; |
||||
} |
||||
|
||||
OC::$CLASSPATH['OC_Filestorage_Archive']='apps/files_archive/lib/storage.php'; |
||||
@ -0,0 +1,10 @@ |
||||
<?xml version="1.0"?> |
||||
<info> |
||||
<id>files_archive</id> |
||||
<name>Archive support</name> |
||||
<description>Transparent opening of archives</description> |
||||
<version>0.1</version> |
||||
<licence>AGPL</licence> |
||||
<author>Robin Appelman</author> |
||||
<require>3</require> |
||||
</info> |
||||
@ -0,0 +1,99 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
abstract class OC_Archive{ |
||||
/** |
||||
* open any of the supporeted archive types |
||||
* @param string path |
||||
* @return OC_Archive |
||||
*/ |
||||
public static function open($path){ |
||||
$ext=substr($path,strrpos($path,'.')); |
||||
switch($ext){ |
||||
case '.zip': |
||||
return new OC_Archive_ZIP($path); |
||||
} |
||||
} |
||||
|
||||
abstract function __construct($source); |
||||
/** |
||||
* add an empty folder to the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
abstract function addFolder($path); |
||||
/** |
||||
* add a file to the archive |
||||
* @param string path |
||||
* @param string source either a local file or string data |
||||
* @return bool |
||||
*/ |
||||
abstract function addFile($path,$source=''); |
||||
/** |
||||
* rename a file or folder in the archive |
||||
* @param string source |
||||
* @param string dest |
||||
* @return bool |
||||
*/ |
||||
abstract function rename($source,$dest); |
||||
/** |
||||
* get the uncompressed size of a file in the archive |
||||
* @param string path |
||||
* @return int |
||||
*/ |
||||
abstract function filesize($path); |
||||
/** |
||||
* get the last modified time of a file in the archive |
||||
* @param string path |
||||
* @return int |
||||
*/ |
||||
abstract function mtime($path); |
||||
/** |
||||
* get the files in a folder |
||||
* @param path |
||||
* @return array |
||||
*/ |
||||
abstract function getFolder($path); |
||||
/** |
||||
*get all files in the archive |
||||
* @return array |
||||
*/ |
||||
abstract function getFiles(); |
||||
/** |
||||
* get the content of a file |
||||
* @param string path |
||||
* @return string |
||||
*/ |
||||
abstract function getFile($path); |
||||
/** |
||||
* extract a single file from the archive |
||||
* @param string path |
||||
* @param string dest |
||||
* @return bool |
||||
*/ |
||||
abstract function extractFile($path,$dest); |
||||
/** |
||||
* check if a file or folder exists in the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
abstract function fileExists($path); |
||||
/** |
||||
* remove a file or folder from the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
abstract function remove($path); |
||||
/** |
||||
* get a file handler |
||||
* @param string path |
||||
* @param string mode |
||||
* @return resource |
||||
*/ |
||||
abstract function getStream($path,$mode); |
||||
} |
||||
@ -0,0 +1,102 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
class OC_Filestorage_Archive extends OC_Filestorage_Common{ |
||||
/** |
||||
* underlying local storage used for missing functions |
||||
* @var OC_Archive |
||||
*/ |
||||
private $archive; |
||||
private $path; |
||||
|
||||
private function stripPath($path){//files should never start with / |
||||
if(substr($path,0,1)=='/'){ |
||||
return substr($path,1); |
||||
} |
||||
return $path; |
||||
} |
||||
|
||||
public function __construct($params){ |
||||
$this->archive=OC_Archive::open($params['archive']); |
||||
$this->path=$params['archive']; |
||||
} |
||||
|
||||
public function mkdir($path){ |
||||
$path=$this->stripPath($path); |
||||
return $this->archive->addFolder($path); |
||||
} |
||||
public function rmdir($path){ |
||||
$path=$this->stripPath($path); |
||||
return $this->archive->remove($path.'/'); |
||||
} |
||||
public function opendir($path){ |
||||
$path=$this->stripPath($path); |
||||
$content=$this->archive->getFolder($path); |
||||
foreach($content as &$file){ |
||||
if(substr($file,-1)=='/'){ |
||||
$file=substr($file,0,-1); |
||||
} |
||||
} |
||||
$id=md5($this->path.$path); |
||||
OC_FakeDirStream::$dirs[$id]=$content; |
||||
return opendir('fakedir://'.$id); |
||||
} |
||||
public function stat($path){ |
||||
$ctime=filectime($this->path); |
||||
$path=$this->stripPath($path); |
||||
if($path==''){ |
||||
$stat=stat($this->path); |
||||
}else{ |
||||
$stat=array(); |
||||
$stat['mtime']=$this->archive->mtime($path); |
||||
$stat['size']=$this->archive->filesize($path); |
||||
} |
||||
$stat['ctime']=$ctime; |
||||
return $stat; |
||||
} |
||||
public function filetype($path){ |
||||
$path=$this->stripPath($path); |
||||
if($path==''){ |
||||
return 'dir'; |
||||
} |
||||
return $this->archive->fileExists($path.'/')?'dir':'file'; |
||||
} |
||||
public function is_readable($path){ |
||||
return is_readable($this->path); |
||||
} |
||||
public function is_writable($path){ |
||||
return is_writable($this->path); |
||||
} |
||||
public function file_exists($path){ |
||||
$path=$this->stripPath($path); |
||||
if($path==''){ |
||||
return file_exists($this->path); |
||||
} |
||||
return $this->archive->fileExists($path) or $this->archive->fileExists($path.'/'); |
||||
} |
||||
public function unlink($path){ |
||||
$path=$this->stripPath($path); |
||||
return $this->archive->remove($path); |
||||
} |
||||
public function fopen($path,$mode){ |
||||
$path=$this->stripPath($path); |
||||
return $this->archive->getStream($path,$mode); |
||||
} |
||||
public function free_space($path){ |
||||
return 0; |
||||
} |
||||
public function touch($path, $mtime=null){ |
||||
if(is_null($mtime)){ |
||||
$tmpFile=OC_Helper::tmpFile(); |
||||
$this->archive->extractFile($path,$tmpFile); |
||||
$this->archive->addfile($path,$tmpFile); |
||||
}else{ |
||||
return false;//not supported |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,182 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
class OC_Archive_ZIP extends OC_Archive{ |
||||
/** |
||||
* @var ZipArchive zip |
||||
*/ |
||||
private $zip=null; |
||||
private $contents=array(); |
||||
private $success=false; |
||||
private $path; |
||||
|
||||
function __construct($source){ |
||||
$this->path=$source; |
||||
$this->zip=new ZipArchive(); |
||||
if($this->zip->open($source,ZipArchive::CREATE)){ |
||||
}else{ |
||||
OC_LOG::write('files_archive','Error while opening archive '.$source,OC_Log::WARN); |
||||
} |
||||
} |
||||
/** |
||||
* add an empty folder to the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
function addFolder($path){ |
||||
return $this->zip->addEmptyDir($path); |
||||
} |
||||
/** |
||||
* add a file to the archive |
||||
* @param string path |
||||
* @param string source either a local file or string data |
||||
* @return bool |
||||
*/ |
||||
function addFile($path,$source=''){ |
||||
if(file_exists($source)){ |
||||
$result=$this->zip->addFile($source,$path); |
||||
}else{ |
||||
$result=$this->zip->addFromString($path,$source); |
||||
} |
||||
if($result){ |
||||
$this->zip->close();//close and reopen to save the zip |
||||
$this->zip->open($this->path); |
||||
} |
||||
return $result; |
||||
} |
||||
/** |
||||
* rename a file or folder in the archive |
||||
* @param string source |
||||
* @param string dest |
||||
* @return bool |
||||
*/ |
||||
function rename($source,$dest){ |
||||
return $this->zip->renameName($source,$dest); |
||||
} |
||||
/** |
||||
* get the uncompressed size of a file in the archive |
||||
* @param string path |
||||
* @return int |
||||
*/ |
||||
function filesize($path){ |
||||
$stat=$this->zip->statName($path); |
||||
return $stat['size']; |
||||
} |
||||
/** |
||||
* get the last modified time of a file in the archive |
||||
* @param string path |
||||
* @return int |
||||
*/ |
||||
function mtime($path){ |
||||
$stat=$this->zip->statName($path); |
||||
return $stat['mtime']; |
||||
} |
||||
/** |
||||
* get the files in a folder |
||||
* @param path |
||||
* @return array |
||||
*/ |
||||
function getFolder($path){ |
||||
$files=$this->getFiles(); |
||||
$folderContent=array(); |
||||
$pathLength=strlen($path); |
||||
foreach($files as $file){ |
||||
if(substr($file,0,$pathLength)==$path and $file!=$path){ |
||||
if(strrpos(substr($file,0,-1),'/')<=$pathLength){ |
||||
$folderContent[]=substr($file,$pathLength); |
||||
} |
||||
} |
||||
} |
||||
return $folderContent; |
||||
} |
||||
/** |
||||
*get all files in the archive |
||||
* @return array |
||||
*/ |
||||
function getFiles(){ |
||||
if(count($this->contents)){ |
||||
return $this->contents; |
||||
} |
||||
$fileCount=$this->zip->numFiles; |
||||
$files=array(); |
||||
for($i=0;$i<$fileCount;$i++){ |
||||
$files[]=$this->zip->getNameIndex($i); |
||||
} |
||||
$this->contents=$files; |
||||
return $files; |
||||
} |
||||
/** |
||||
* get the content of a file |
||||
* @param string path |
||||
* @return string |
||||
*/ |
||||
function getFile($path){ |
||||
return $this->zip->getFromName($path); |
||||
} |
||||
/** |
||||
* extract a single file from the archive |
||||
* @param string path |
||||
* @param string dest |
||||
* @return bool |
||||
*/ |
||||
function extractFile($path,$dest){ |
||||
$fp = $this->zip->getStream($path); |
||||
file_put_contents($dest,$fp); |
||||
} |
||||
/** |
||||
* check if a file or folder exists in the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
function fileExists($path){ |
||||
return $this->zip->locateName($path)!==false; |
||||
} |
||||
/** |
||||
* remove a file or folder from the archive |
||||
* @param string path |
||||
* @return bool |
||||
*/ |
||||
function remove($path){ |
||||
return $this->zip->deleteName($path); |
||||
} |
||||
/** |
||||
* get a file handler |
||||
* @param string path |
||||
* @param string mode |
||||
* @return resource |
||||
*/ |
||||
function getStream($path,$mode){ |
||||
if($mode=='r' or $mode=='rb'){ |
||||
return $this->zip->getStream($path); |
||||
}else{//since we cant directly get a writable stream, make a temp copy of the file and put it back in the archive when the stream is closed |
||||
if(strrpos($path,'.')!==false){ |
||||
$ext=substr($path,strrpos($path,'.')); |
||||
}else{ |
||||
$ext=''; |
||||
} |
||||
$tmpFile=OC_Helper::tmpFile($ext); |
||||
OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); |
||||
if($this->fileExists($path)){ |
||||
$this->extractFile($path,$tmpFile); |
||||
} |
||||
self::$tempFiles[$tmpFile]=$path; |
||||
return fopen('close://'.$tmpFile,$mode); |
||||
} |
||||
} |
||||
|
||||
private static $tempFiles=array(); |
||||
/** |
||||
* write back temporary files |
||||
*/ |
||||
function writeBack($tmpFile){ |
||||
if(isset(self::$tempFiles[$tmpFile])){ |
||||
$this->addFile(self::$tempFiles[$tmpFile],$tmpFile); |
||||
unlink($tmpFile); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,97 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
abstract class Test_Archive extends UnitTestCase { |
||||
/** |
||||
* @var OC_Archive |
||||
*/ |
||||
protected $instance; |
||||
|
||||
/** |
||||
* get the existing test archive |
||||
* @return OC_Archive |
||||
*/ |
||||
abstract protected function getExisting(); |
||||
/** |
||||
* get a new archive for write testing |
||||
* @return OC_Archive |
||||
*/ |
||||
abstract protected function getNew(); |
||||
|
||||
public function testGetFiles(){ |
||||
$this->instance=$this->getExisting(); |
||||
$allFiles=$this->instance->getFiles(); |
||||
$expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt'); |
||||
$this->assertEqual(4,count($allFiles)); |
||||
foreach($expected as $file){ |
||||
$this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive'); |
||||
$this->assertTrue($this->instance->fileExists($file)); |
||||
} |
||||
$this->assertFalse($this->instance->fileExists('non/existing/file')); |
||||
|
||||
$rootContent=$this->instance->getFolder(''); |
||||
$expected=array('lorem.txt','logo-wide.png','dir/'); |
||||
$this->assertEqual(3,count($rootContent)); |
||||
foreach($expected as $file){ |
||||
$this->assertNotIdentical(false,array_search($file,$rootContent),'cant find '.$file.' in archive'); |
||||
} |
||||
|
||||
$dirContent=$this->instance->getFolder('dir/'); |
||||
$expected=array('lorem.txt'); |
||||
$this->assertEqual(1,count($dirContent)); |
||||
foreach($expected as $file){ |
||||
$this->assertNotIdentical(false,array_search($file,$dirContent),'cant find '.$file.' in archive'); |
||||
} |
||||
} |
||||
|
||||
public function testContent(){ |
||||
$this->instance=$this->getExisting(); |
||||
$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; |
||||
$textFile=$dir.'/lorem.txt'; |
||||
$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); |
||||
|
||||
$tmpFile=OC_Helper::tmpFile('.txt'); |
||||
$this->instance->extractFile('lorem.txt',$tmpFile); |
||||
$this->assertEqual(file_get_contents($textFile),file_get_contents($tmpFile)); |
||||
} |
||||
|
||||
public function testWrite(){ |
||||
$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; |
||||
$textFile=$dir.'/lorem.txt'; |
||||
$this->instance=$this->getNew(); |
||||
$this->assertEqual(0,count($this->instance->getFiles())); |
||||
$this->instance->addFile('lorem.txt',$textFile); |
||||
$this->assertEqual(1,count($this->instance->getFiles())); |
||||
$this->assertTrue($this->instance->fileExists('lorem.txt')); |
||||
|
||||
$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); |
||||
$this->instance->addFile('lorem.txt','foobar'); |
||||
$this->assertEqual('foobar',$this->instance->getFile('lorem.txt')); |
||||
} |
||||
|
||||
public function testReadStream(){ |
||||
$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; |
||||
$this->instance=$this->getExisting(); |
||||
$fh=$this->instance->getStream('lorem.txt','r'); |
||||
$this->assertTrue($fh); |
||||
$content=fread($fh,$this->instance->filesize('lorem.txt')); |
||||
fclose($fh); |
||||
$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$content); |
||||
} |
||||
public function testWriteStream(){ |
||||
$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; |
||||
$this->instance=$this->getNew(); |
||||
$fh=$this->instance->getStream('lorem.txt','w'); |
||||
$source=fopen($dir.'/lorem.txt','r'); |
||||
OC_Helper::streamCopy($source,$fh); |
||||
fclose($source); |
||||
fclose($fh); |
||||
$this->assertTrue($this->instance->fileExists('lorem.txt')); |
||||
$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt')); |
||||
} |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
class Test_Filestorage_Archive_Zip extends Test_FileStorage { |
||||
/** |
||||
* @var string tmpDir |
||||
*/ |
||||
public function setUp(){ |
||||
$tmpFile=OC_Helper::tmpFile('.zip'); |
||||
$this->instance=new OC_Filestorage_Archive(array('archive'=>$tmpFile)); |
||||
} |
||||
} |
||||
|
||||
?> |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
require_once('archive.php'); |
||||
|
||||
class Test_Archive_ZIP extends Test_Archive{ |
||||
protected function getExisting(){ |
||||
$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; |
||||
return new OC_Archive_ZIP($dir.'/data.zip'); |
||||
} |
||||
|
||||
protected function getNew(){ |
||||
return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip')); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue