You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
5.8 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bart Visscher <bartv@thisnet.nl>
  7. * @author Christopher Schäpers <kondou@ts.unde.re>
  8. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  11. * @author Morris Jobke <hey@morrisjobke.de>
  12. * @author Robin Appelman <robin@icewind.nl>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Stefan Weil <sw@weilnetz.de>
  15. * @author Thomas Müller <thomas.mueller@tmit.eu>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OC\Archive;
  33. use Icewind\Streams\CallbackWrapper;
  34. use OCP\ILogger;
  35. class ZIP extends Archive {
  36. /**
  37. * @var \ZipArchive zip
  38. */
  39. private $zip=null;
  40. private $path;
  41. /**
  42. * @param string $source
  43. */
  44. public function __construct($source) {
  45. $this->path=$source;
  46. $this->zip=new \ZipArchive();
  47. if ($this->zip->open($source, \ZipArchive::CREATE)) {
  48. } else {
  49. \OCP\Util::writeLog('files_archive', 'Error while opening archive '.$source, ILogger::WARN);
  50. }
  51. }
  52. /**
  53. * add an empty folder to the archive
  54. * @param string $path
  55. * @return bool
  56. */
  57. public function addFolder($path) {
  58. return $this->zip->addEmptyDir($path);
  59. }
  60. /**
  61. * add a file to the archive
  62. * @param string $path
  63. * @param string $source either a local file or string data
  64. * @return bool
  65. */
  66. public function addFile($path, $source='') {
  67. if ($source and $source[0]=='/' and file_exists($source)) {
  68. $result=$this->zip->addFile($source, $path);
  69. } else {
  70. $result=$this->zip->addFromString($path, $source);
  71. }
  72. if ($result) {
  73. $this->zip->close();//close and reopen to save the zip
  74. $this->zip->open($this->path);
  75. }
  76. return $result;
  77. }
  78. /**
  79. * rename a file or folder in the archive
  80. * @param string $source
  81. * @param string $dest
  82. * @return boolean|null
  83. */
  84. public function rename($source, $dest) {
  85. $source=$this->stripPath($source);
  86. $dest=$this->stripPath($dest);
  87. $this->zip->renameName($source, $dest);
  88. }
  89. /**
  90. * get the uncompressed size of a file in the archive
  91. * @param string $path
  92. * @return int
  93. */
  94. public function filesize($path) {
  95. $stat=$this->zip->statName($path);
  96. return $stat['size'];
  97. }
  98. /**
  99. * get the last modified time of a file in the archive
  100. * @param string $path
  101. * @return int
  102. */
  103. public function mtime($path) {
  104. return filemtime($this->path);
  105. }
  106. /**
  107. * get the files in a folder
  108. * @param string $path
  109. * @return array
  110. */
  111. public function getFolder($path) {
  112. $files=$this->getFiles();
  113. $folderContent=[];
  114. $pathLength=strlen($path);
  115. foreach ($files as $file) {
  116. if (substr($file, 0, $pathLength)==$path and $file!=$path) {
  117. if (strrpos(substr($file, 0, -1), '/')<=$pathLength) {
  118. $folderContent[]=substr($file, $pathLength);
  119. }
  120. }
  121. }
  122. return $folderContent;
  123. }
  124. /**
  125. * get all files in the archive
  126. * @return array
  127. */
  128. public function getFiles() {
  129. $fileCount=$this->zip->numFiles;
  130. $files=[];
  131. for ($i=0;$i<$fileCount;$i++) {
  132. $files[]=$this->zip->getNameIndex($i);
  133. }
  134. return $files;
  135. }
  136. /**
  137. * get the content of a file
  138. * @param string $path
  139. * @return string
  140. */
  141. public function getFile($path) {
  142. return $this->zip->getFromName($path);
  143. }
  144. /**
  145. * extract a single file from the archive
  146. * @param string $path
  147. * @param string $dest
  148. * @return boolean|null
  149. */
  150. public function extractFile($path, $dest) {
  151. $fp = $this->zip->getStream($path);
  152. file_put_contents($dest, $fp);
  153. }
  154. /**
  155. * extract the archive
  156. * @param string $dest
  157. * @return bool
  158. */
  159. public function extract($dest) {
  160. return $this->zip->extractTo($dest);
  161. }
  162. /**
  163. * check if a file or folder exists in the archive
  164. * @param string $path
  165. * @return bool
  166. */
  167. public function fileExists($path) {
  168. return ($this->zip->locateName($path)!==false) or ($this->zip->locateName($path.'/')!==false);
  169. }
  170. /**
  171. * remove a file or folder from the archive
  172. * @param string $path
  173. * @return bool
  174. */
  175. public function remove($path) {
  176. if ($this->fileExists($path.'/')) {
  177. return $this->zip->deleteName($path.'/');
  178. } else {
  179. return $this->zip->deleteName($path);
  180. }
  181. }
  182. /**
  183. * get a file handler
  184. * @param string $path
  185. * @param string $mode
  186. * @return resource
  187. */
  188. public function getStream($path, $mode) {
  189. if ($mode=='r' or $mode=='rb') {
  190. return $this->zip->getStream($path);
  191. } else {
  192. //since we can't directly get a writable stream,
  193. //make a temp copy of the file and put it back
  194. //in the archive when the stream is closed
  195. if (strrpos($path, '.')!==false) {
  196. $ext=substr($path, strrpos($path, '.'));
  197. } else {
  198. $ext='';
  199. }
  200. $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
  201. if ($this->fileExists($path)) {
  202. $this->extractFile($path, $tmpFile);
  203. }
  204. $handle = fopen($tmpFile, $mode);
  205. return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
  206. $this->writeBack($tmpFile, $path);
  207. });
  208. }
  209. }
  210. /**
  211. * write back temporary files
  212. */
  213. public function writeBack($tmpFile, $path) {
  214. $this->addFile($path, $tmpFile);
  215. unlink($tmpFile);
  216. }
  217. /**
  218. * @param string $path
  219. * @return string
  220. */
  221. private function stripPath($path) {
  222. if (!$path || $path[0]=='/') {
  223. return substr($path, 1);
  224. } else {
  225. return $path;
  226. }
  227. }
  228. }