tar.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. require_once '3rdparty/Archive/Tar.php';
  9. class OC_Archive_TAR extends OC_Archive{
  10. const PLAIN=0;
  11. const GZIP=1;
  12. const BZIP=2;
  13. private $fileList;
  14. /**
  15. * @var Archive_Tar tar
  16. */
  17. private $tar=null;
  18. private $path;
  19. function __construct($source){
  20. $types=array(null,'gz','bz');
  21. $this->path=$source;
  22. $this->tar=new Archive_Tar($source,$types[self::getTarType($source)]);
  23. }
  24. /**
  25. * try to detect the type of tar compression
  26. * @param string file
  27. * @return str
  28. */
  29. static public function getTarType($file){
  30. if(strpos($file,'.')){
  31. $extension=substr($file,strrpos($file,'.'));
  32. switch($extension){
  33. case 'gz':
  34. case 'tgz':
  35. return self::GZIP;
  36. case 'bz':
  37. case 'bz2':
  38. return self::BZIP;
  39. default:
  40. return self::PLAIN;
  41. }
  42. }else{
  43. return self::PLAIN;
  44. }
  45. }
  46. /**
  47. * add an empty folder to the archive
  48. * @param string path
  49. * @return bool
  50. */
  51. function addFolder($path){
  52. $tmpBase=OC_Helper::tmpFolder();
  53. if(substr($path,-1,1)!='/'){
  54. $path.='/';
  55. }
  56. if($this->fileExists($path)){
  57. return false;
  58. }
  59. $parts=explode('/',$path);
  60. $folder=$tmpBase;
  61. foreach($parts as $part){
  62. $folder.='/'.$part;
  63. if(!is_dir($folder)){
  64. mkdir($folder);
  65. }
  66. }
  67. $result=$this->tar->addModify(array($tmpBase.$path),'',$tmpBase);
  68. rmdir($tmpBase.$path);
  69. $this->fileList=false;
  70. return $result;
  71. }
  72. /**
  73. * add a file to the archive
  74. * @param string path
  75. * @param string source either a local file or string data
  76. * @return bool
  77. */
  78. function addFile($path,$source=''){
  79. if($this->fileExists($path)){
  80. $this->remove($path);
  81. }
  82. if($source and $source[0]=='/' and file_exists($source)){
  83. $header=array();
  84. $dummy='';
  85. $this->tar->_openAppend();
  86. $result=$this->tar->_addfile($source,$header,$dummy,$dummy,$path);
  87. }else{
  88. $result=$this->tar->addString($path,$source);
  89. }
  90. $this->fileList=false;
  91. return $result;
  92. }
  93. /**
  94. * rename a file or folder in the archive
  95. * @param string source
  96. * @param string dest
  97. * @return bool
  98. */
  99. function rename($source,$dest){
  100. //no proper way to delete, rename entire archive, rename file and remake archive
  101. $tmp=OCP\Files::tmpFolder();
  102. $this->tar->extract($tmp);
  103. rename($tmp.$source,$tmp.$dest);
  104. $this->tar=null;
  105. unlink($this->path);
  106. $types=array(null,'gz','bz');
  107. $this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
  108. $this->tar->createModify(array($tmp),'',$tmp.'/');
  109. $this->fileList=false;
  110. return true;
  111. }
  112. private function getHeader($file){
  113. $headers=$this->tar->listContent();
  114. foreach($headers as $header){
  115. if($file==$header['filename'] or $file.'/'==$header['filename'] or '/'.$file.'/'==$header['filename'] or '/'.$file==$header['filename']){
  116. return $header;
  117. }
  118. }
  119. return null;
  120. }
  121. /**
  122. * get the uncompressed size of a file in the archive
  123. * @param string path
  124. * @return int
  125. */
  126. function filesize($path){
  127. $stat=$this->getHeader($path);
  128. return $stat['size'];
  129. }
  130. /**
  131. * get the last modified time of a file in the archive
  132. * @param string path
  133. * @return int
  134. */
  135. function mtime($path){
  136. $stat=$this->getHeader($path);
  137. return $stat['mtime'];
  138. }
  139. /**
  140. * get the files in a folder
  141. * @param path
  142. * @return array
  143. */
  144. function getFolder($path){
  145. $files=$this->getFiles();
  146. $folderContent=array();
  147. $pathLength=strlen($path);
  148. foreach($files as $file){
  149. if($file[0]=='/'){
  150. $file=substr($file,1);
  151. }
  152. if(substr($file,0,$pathLength)==$path and $file!=$path){
  153. $result=substr($file,$pathLength);
  154. if($pos=strpos($result,'/')){
  155. $result=substr($result,0,$pos+1);
  156. }
  157. if(array_search($result,$folderContent)===false){
  158. $folderContent[]=$result;
  159. }
  160. }
  161. }
  162. return $folderContent;
  163. }
  164. /**
  165. *get all files in the archive
  166. * @return array
  167. */
  168. function getFiles(){
  169. if($this->fileList){
  170. return $this->fileList;
  171. }
  172. $headers=$this->tar->listContent();
  173. $files=array();
  174. foreach($headers as $header){
  175. $files[]=$header['filename'];
  176. }
  177. $this->fileList=$files;
  178. return $files;
  179. }
  180. /**
  181. * get the content of a file
  182. * @param string path
  183. * @return string
  184. */
  185. function getFile($path){
  186. return $this->tar->extractInString($path);
  187. }
  188. /**
  189. * extract a single file from the archive
  190. * @param string path
  191. * @param string dest
  192. * @return bool
  193. */
  194. function extractFile($path,$dest){
  195. $tmp=OCP\Files::tmpFolder();
  196. if(!$this->fileExists($path)){
  197. return false;
  198. }
  199. if($this->fileExists('/'.$path)){
  200. $success=$this->tar->extractList(array('/'.$path),$tmp);
  201. }else{
  202. $success=$this->tar->extractList(array($path),$tmp);
  203. }
  204. if($success){
  205. rename($tmp.$path,$dest);
  206. }
  207. OCP\Files::rmdirr($tmp);
  208. return $success;
  209. }
  210. /**
  211. * extract the archive
  212. * @param string path
  213. * @param string dest
  214. * @return bool
  215. */
  216. function extract($dest){
  217. return $this->tar->extract($dest);
  218. }
  219. /**
  220. * check if a file or folder exists in the archive
  221. * @param string path
  222. * @return bool
  223. */
  224. function fileExists($path){
  225. $files=$this->getFiles();
  226. if((array_search($path,$files)!==false) or (array_search($path.'/',$files)!==false)){
  227. return true;
  228. }else{
  229. $folderPath=$path;
  230. if(substr($folderPath,-1,1)!='/'){
  231. $folderPath.='/';
  232. }
  233. $pathLength=strlen($folderPath);
  234. foreach($files as $file){
  235. if(strlen($file)>$pathLength and substr($file,0,$pathLength)==$folderPath){
  236. return true;
  237. }
  238. }
  239. }
  240. if($path[0]!='/'){//not all programs agree on the use of a leading /
  241. return $this->fileExists('/'.$path);
  242. }else{
  243. return false;
  244. }
  245. }
  246. /**
  247. * remove a file or folder from the archive
  248. * @param string path
  249. * @return bool
  250. */
  251. function remove($path){
  252. if(!$this->fileExists($path)){
  253. return false;
  254. }
  255. $this->fileList=false;
  256. //no proper way to delete, extract entire archive, delete file and remake archive
  257. $tmp=OCP\Files::tmpFolder();
  258. $this->tar->extract($tmp);
  259. OCP\Files::rmdirr($tmp.$path);
  260. $this->tar=null;
  261. unlink($this->path);
  262. $this->reopen();
  263. $this->tar->createModify(array($tmp),'',$tmp);
  264. return true;
  265. }
  266. /**
  267. * get a file handler
  268. * @param string path
  269. * @param string mode
  270. * @return resource
  271. */
  272. function getStream($path,$mode){
  273. if(strrpos($path,'.')!==false){
  274. $ext=substr($path,strrpos($path,'.'));
  275. }else{
  276. $ext='';
  277. }
  278. $tmpFile=OCP\Files::tmpFile($ext);
  279. if($this->fileExists($path)){
  280. $this->extractFile($path,$tmpFile);
  281. }elseif($mode=='r' or $mode=='rb'){
  282. return false;
  283. }
  284. if($mode=='r' or $mode=='rb'){
  285. return fopen($tmpFile,$mode);
  286. }else{
  287. OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
  288. self::$tempFiles[$tmpFile]=$path;
  289. return fopen('close://'.$tmpFile,$mode);
  290. }
  291. }
  292. private static $tempFiles=array();
  293. /**
  294. * write back temporary files
  295. */
  296. function writeBack($tmpFile){
  297. if(isset(self::$tempFiles[$tmpFile])){
  298. $this->addFile(self::$tempFiles[$tmpFile],$tmpFile);
  299. unlink($tmpFile);
  300. }
  301. }
  302. /**
  303. * reopen the archive to ensure everything is written
  304. */
  305. private function reopen(){
  306. if($this->tar){
  307. $this->tar->_close();
  308. $this->tar=null;
  309. }
  310. $types=array(null,'gz','bz');
  311. $this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
  312. }
  313. }