1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Filter 17 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 * @version $Id$ 20 */ 21 22/** 23 * @see Zend_Filter_Compress_CompressAbstract 24 */ 25 26/** 27 * Compression adapter for zip 28 * 29 * @category Zend 30 * @package Zend_Filter 31 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 32 * @license http://framework.zend.com/license/new-bsd New BSD License 33 */ 34class Zend_Filter_Compress_Zip extends Zend_Filter_Compress_CompressAbstract 35{ 36 /** 37 * Compression Options 38 * array( 39 * 'archive' => Archive to use 40 * 'password' => Password to use 41 * 'target' => Target to write the files to 42 * ) 43 * 44 * @var array 45 */ 46 protected $_options = array( 47 'archive' => null, 48 'target' => null, 49 ); 50 51 /** 52 * Class constructor 53 * 54 * @param string|array $options (Optional) Options to set 55 */ 56 public function __construct($options = null) 57 { 58 if (!extension_loaded('zip')) { 59 throw new Zend_Filter_Exception('This filter needs the zip extension'); 60 } 61 parent::__construct($options); 62 } 63 64 /** 65 * Returns the set archive 66 * 67 * @return string 68 */ 69 public function getArchive() 70 { 71 return $this->_options['archive']; 72 } 73 74 /** 75 * Sets the archive to use for de-/compression 76 * 77 * @param string $archive Archive to use 78 * @return Zend_Filter_Compress_Rar 79 */ 80 public function setArchive($archive) 81 { 82 $archive = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $archive); 83 $this->_options['archive'] = (string) $archive; 84 85 return $this; 86 } 87 88 /** 89 * Returns the set targetpath 90 * 91 * @return string 92 */ 93 public function getTarget() 94 { 95 return $this->_options['target']; 96 } 97 98 /** 99 * Sets the target to use 100 * 101 * @param string $target 102 * @return Zend_Filter_Compress_Rar 103 */ 104 public function setTarget($target) 105 { 106 if (!file_exists(dirname($target))) { 107 throw new Zend_Filter_Exception("The directory '$target' does not exist"); 108 } 109 110 $target = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $target); 111 $this->_options['target'] = (string) $target; 112 return $this; 113 } 114 115 /** 116 * Compresses the given content 117 * 118 * @param string $content 119 * @return string Compressed archive 120 */ 121 public function compress($content) 122 { 123 $zip = new ZipArchive(); 124 $res = $zip->open($this->getArchive(), ZipArchive::CREATE | ZipArchive::OVERWRITE); 125 126 if ($res !== true) { 127 throw new Zend_Filter_Exception($this->_errorString($res)); 128 } 129 130 if (file_exists($content)) { 131 $content = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, realpath($content)); 132 $basename = substr($content, strrpos($content, DIRECTORY_SEPARATOR) + 1); 133 if (is_dir($content)) { 134 $index = strrpos($content, DIRECTORY_SEPARATOR) + 1; 135 $content .= DIRECTORY_SEPARATOR; 136 $stack = array($content); 137 while (!empty($stack)) { 138 $current = array_pop($stack); 139 $files = array(); 140 141 $dir = dir($current); 142 while (false !== ($node = $dir->read())) { 143 if (($node == '.') || ($node == '..')) { 144 continue; 145 } 146 147 if (is_dir($current . $node)) { 148 array_push($stack, $current . $node . DIRECTORY_SEPARATOR); 149 } 150 151 if (is_file($current . $node)) { 152 $files[] = $node; 153 } 154 } 155 156 $local = substr($current, $index); 157 $zip->addEmptyDir(substr($local, 0, -1)); 158 159 foreach ($files as $file) { 160 $zip->addFile($current . $file, $local . $file); 161 if ($res !== true) { 162 throw new Zend_Filter_Exception($this->_errorString($res)); 163 } 164 } 165 } 166 } else { 167 $res = $zip->addFile($content, $basename); 168 if ($res !== true) { 169 throw new Zend_Filter_Exception($this->_errorString($res)); 170 } 171 } 172 } else { 173 $file = $this->getTarget(); 174 if (!is_dir($file)) { 175 $file = basename($file); 176 } else { 177 $file = "zip.tmp"; 178 } 179 180 $res = $zip->addFromString($file, $content); 181 if ($res !== true) { 182 throw new Zend_Filter_Exception($this->_errorString($res)); 183 } 184 } 185 186 $zip->close(); 187 return $this->_options['archive']; 188 } 189 190 /** 191 * Decompresses the given content 192 * 193 * @param string $content 194 * @return string 195 */ 196 public function decompress($content) 197 { 198 $archive = $this->getArchive(); 199 if (file_exists($content)) { 200 $archive = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, realpath($content)); 201 } elseif (empty($archive) || !file_exists($archive)) { 202 throw new Zend_Filter_Exception('ZIP Archive not found'); 203 } 204 205 $zip = new ZipArchive(); 206 $res = $zip->open($archive); 207 208 $target = $this->getTarget(); 209 210 if (!empty($target) && !is_dir($target)) { 211 $target = dirname($target); 212 } 213 214 if (!empty($target)) { 215 $target = rtrim($target, '/\\') . DIRECTORY_SEPARATOR; 216 } 217 218 if (empty($target) || !is_dir($target)) { 219 throw new Zend_Filter_Exception('No target for ZIP decompression set'); 220 } 221 222 if ($res !== true) { 223 throw new Zend_Filter_Exception($this->_errorString($res)); 224 } 225 226 if (version_compare(PHP_VERSION, '5.2.8', '<')) { 227 for ($i = 0; $i < $zip->numFiles; $i++) { 228 $statIndex = $zip->statIndex($i); 229 $currName = $statIndex['name']; 230 if (($currName[0] == '/') || 231 (substr($currName, 0, 2) == '..') || 232 (substr($currName, 0, 4) == './..') 233 ) 234 { 235 throw new Zend_Filter_Exception('Upward directory traversal was detected inside ' . $archive 236 . ' please use PHP 5.2.8 or greater to take advantage of path resolution features of ' 237 . 'the zip extension in this decompress() method.' 238 ); 239 } 240 } 241 } 242 243 $res = @$zip->extractTo($target); 244 if ($res !== true) { 245 throw new Zend_Filter_Exception($this->_errorString($res)); 246 } 247 248 $zip->close(); 249 return $target; 250 } 251 252 /** 253 * Returns the proper string based on the given error constant 254 * 255 * @param string $error 256 */ 257 protected function _errorString($error) 258 { 259 switch($error) { 260 case ZipArchive::ER_MULTIDISK : 261 return 'Multidisk ZIP Archives not supported'; 262 263 case ZipArchive::ER_RENAME : 264 return 'Failed to rename the temporary file for ZIP'; 265 266 case ZipArchive::ER_CLOSE : 267 return 'Failed to close the ZIP Archive'; 268 269 case ZipArchive::ER_SEEK : 270 return 'Failure while seeking the ZIP Archive'; 271 272 case ZipArchive::ER_READ : 273 return 'Failure while reading the ZIP Archive'; 274 275 case ZipArchive::ER_WRITE : 276 return 'Failure while writing the ZIP Archive'; 277 278 case ZipArchive::ER_CRC : 279 return 'CRC failure within the ZIP Archive'; 280 281 case ZipArchive::ER_ZIPCLOSED : 282 return 'ZIP Archive already closed'; 283 284 case ZipArchive::ER_NOENT : 285 return 'No such file within the ZIP Archive'; 286 287 case ZipArchive::ER_EXISTS : 288 return 'ZIP Archive already exists'; 289 290 case ZipArchive::ER_OPEN : 291 return 'Can not open ZIP Archive'; 292 293 case ZipArchive::ER_TMPOPEN : 294 return 'Failure creating temporary ZIP Archive'; 295 296 case ZipArchive::ER_ZLIB : 297 return 'ZLib Problem'; 298 299 case ZipArchive::ER_MEMORY : 300 return 'Memory allocation problem while working on a ZIP Archive'; 301 302 case ZipArchive::ER_CHANGED : 303 return 'ZIP Entry has been changed'; 304 305 case ZipArchive::ER_COMPNOTSUPP : 306 return 'Compression method not supported within ZLib'; 307 308 case ZipArchive::ER_EOF : 309 return 'Premature EOF within ZIP Archive'; 310 311 case ZipArchive::ER_INVAL : 312 return 'Invalid argument for ZLIB'; 313 314 case ZipArchive::ER_NOZIP : 315 return 'Given file is no zip archive'; 316 317 case ZipArchive::ER_INTERNAL : 318 return 'Internal error while working on a ZIP Archive'; 319 320 case ZipArchive::ER_INCONS : 321 return 'Inconsistent ZIP archive'; 322 323 case ZipArchive::ER_REMOVE : 324 return 'Can not remove ZIP Archive'; 325 326 case ZipArchive::ER_DELETED : 327 return 'ZIP Entry has been deleted'; 328 329 default : 330 return 'Unknown error within ZIP Archive'; 331 } 332 } 333 334 /** 335 * Returns the adapter name 336 * 337 * @return string 338 */ 339 public function toString() 340 { 341 return 'Zip'; 342 } 343} 344