| [ Index ] |
PHP Cross Reference of Akelos Framework |
[Summary view] [Print] [Text view]
1 <?php 2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 // +----------------------------------------------------------------------+ 4 // | Akelos Framework - http://www.akelos.org | 5 // +----------------------------------------------------------------------+ 6 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez | 7 // | Released under the GNU Lesser General Public License, see LICENSE.txt| 8 // +----------------------------------------------------------------------+ 9 10 /** 11 * @package ActiveSupport 12 * @subpackage Utils 13 * @author Bermi Ferrer <bermi a.t akelos c.om> 14 * @author Vincent Blavet <vincent@blavet.net> 15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org 16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html> 17 */ 18 19 20 require_once 'PEAR.php'; 21 // ----- Constants 22 define('AK_ZIP_READ_BLOCK_SIZE', 2048); 23 // ----- File list separator 24 define('AK_ZIP_SEPARATOR', ','); 25 // ----- Optional static temporary directory 26 // By default temporary files are generated in the script current 27 // path. 28 // If defined : 29 // - MUST BE terminated by a '/'. 30 // - MUST be a valid, already created directory 31 // Samples : 32 // define( 'AK_ZIP_TEMPORARY_DIR', '/temp/' ); 33 // define( 'AK_ZIP_TEMPORARY_DIR', 'C:/Temp/' ); 34 define('AK_ZIP_TEMPORARY_DIR', ''); 35 // ----- Error codes 36 define('AK_ZIP_ERR_NO_ERROR', 0); 37 define('AK_ZIP_ERR_WRITE_OPEN_FAIL', -1); 38 define('AK_ZIP_ERR_READ_OPEN_FAIL', -2); 39 define('AK_ZIP_ERR_INVALID_PARAMETER', -3); 40 define('AK_ZIP_ERR_MISSING_FILE', -4); 41 define('AK_ZIP_ERR_FILENAME_TOO_LONG', -5); 42 define('AK_ZIP_ERR_INVALID_ZIP', -6); 43 define('AK_ZIP_ERR_BAD_EXTRACTED_FILE', -7); 44 define('AK_ZIP_ERR_DIR_CREATE_FAIL', -8); 45 define('AK_ZIP_ERR_BAD_EXTENSION', -9); 46 define('AK_ZIP_ERR_BAD_FORMAT', -10); 47 define('AK_ZIP_ERR_DELETE_FILE_FAIL', -11); 48 define('AK_ZIP_ERR_RENAME_FILE_FAIL', -12); 49 define('AK_ZIP_ERR_BAD_CHECKSUM', -13); 50 define('AK_ZIP_ERR_INVALID_AK_ZIP', -14); 51 define('AK_ZIP_ERR_MISSING_OPTION_VALUE', -15); 52 define('AK_ZIP_ERR_INVALID_PARAM_VALUE', -16); 53 // ----- Warning codes 54 define('AK_ZIP_WARN_NO_WARNING', 0); 55 define('AK_ZIP_WARN_FILE_EXIST', 1); 56 // ----- Methods parameters 57 define('AK_ZIP_PARAM_PATH', 'path'); 58 define('AK_ZIP_PARAM_ADD_PATH', 'add_path'); 59 define('AK_ZIP_PARAM_REMOVE_PATH', 'remove_path'); 60 define('AK_ZIP_PARAM_REMOVE_ALL_PATH', 'remove_all_path'); 61 define('AK_ZIP_PARAM_SET_CHMOD', 'set_chmod'); 62 define('AK_ZIP_PARAM_EXTRACT_AS_STRING', 'extract_as_string'); 63 define('AK_ZIP_PARAM_NO_COMPRESSION', 'no_compression'); 64 define('AK_ZIP_PARAM_BY_NAME', 'by_name'); 65 define('AK_ZIP_PARAM_BY_INDEX', 'by_index'); 66 define('AK_ZIP_PARAM_BY_EREG', 'by_ereg'); 67 define('AK_ZIP_PARAM_BY_PREG', 'by_preg'); 68 define('AK_ZIP_PARAM_PRE_EXTRACT', 'callback_pre_extract'); 69 define('AK_ZIP_PARAM_POST_EXTRACT', 'callback_post_extract'); 70 define('AK_ZIP_PARAM_PRE_ADD', 'callback_pre_add'); 71 define('AK_ZIP_PARAM_POST_ADD', 'callback_post_add'); 72 /** 73 * Class for manipulating zip archive files 74 * 75 * A class which provided common methods to manipulate ZIP formatted 76 * archive files. 77 * It provides creation, extraction, deletion and add features. 78 * 79 * @author Vincent Blavet <vincent@blavet.net> 80 * @version $Revision: 1.6 $ 81 * @category Archive 82 */ 83 84 class AkZip 85 { 86 /** 87 * The filename of the zip archive. 88 * 89 * @var string Name of the Zip file 90 */ 91 var $_zipname = ''; 92 /** 93 * File descriptor of the opened Zip file. 94 * 95 * @var int Internal zip file descriptor 96 */ 97 var $_zip_fd = 0; 98 /** 99 * @var int last error code 100 */ 101 var $_error_code = 1; 102 /** 103 * @var string Last error description 104 */ 105 var $_error_string = ''; 106 // {{{ constructor 107 108 /** 109 * AkZip Class constructor. This flavour of the constructor only 110 * declare a new AkZip object, identifying it by the name of the 111 * zip file. 112 * 113 * @param string $p_zipname The name of the zip archive to create 114 * @access public 115 */ 116 117 function AkZip($p_zipname) 118 { 119 // ----- Check the zlib 120 if (!extension_loaded('zlib')) { 121 PEAR::loadExtension('zlib'); 122 } 123 if (!extension_loaded('zlib')) { 124 die("The extension 'zlib' couldn't be found.\n"."Please make sure your version of PHP was built "."with 'zlib' support.\n"); 125 return false; 126 } 127 // ----- Set the attributes 128 $this->_zipname = $p_zipname; 129 $this->_zip_fd = 0; 130 return; 131 } 132 // }}} 133 // {{{ create() 134 135 /** 136 * This method creates a Zip Archive with the filename set with 137 * the constructor. 138 * The files and directories indicated in $p_filelist 139 * are added in the archive. 140 * When a directory is in the list, the directory and its content is added 141 * in the archive. 142 * The methods takes a variable list of parameters in $p_params. 143 * The supported parameters for this method are : 144 * 'add_path' : Add a path to the archived files. 145 * 'remove_path' : Remove the specified 'root' path of the archived files. 146 * 'remove_all_path' : Remove all the path of the archived files. 147 * 'no_compression' : The archived files will not be compressed. 148 * 149 * @access public 150 * @param mixed $p_filelist The list of the files or folders to add. 151 * It can be a string with filenames separated 152 * by a comma, or an array of filenames. 153 * @param mixed $p_params An array of variable parameters and values. 154 * @return mixed An array of file description on success, 155 * an error code on error 156 */ 157 158 function create($p_filelist, $p_params = 0) 159 { 160 $this->_errorReset(); 161 // ----- Set default values 162 if ($p_params === 0) { 163 $p_params = array(); 164 } 165 if ($this->_check_parameters($p_params, array( 166 'no_compression' => false, 167 'add_path' => "", 168 'remove_path' => "", 169 'remove_all_path' => false 170 )) != 1) { 171 return 0; 172 } 173 // ----- Look if the $p_filelist is really an array 174 $p_result_list = array(); 175 if (is_array($p_filelist)) { 176 $v_result = $this->_create($p_filelist, $p_result_list, $p_params); 177 } 178 // ----- Look if the $p_filelist is a string 179 else if (is_string($p_filelist)) { 180 // ----- Create a list with the elements from the string 181 $v_list = explode(AK_ZIP_SEPARATOR, $p_filelist); 182 $v_result = $this->_create($v_list, $p_result_list, $p_params); 183 } 184 // ----- Invalid variable 185 else { 186 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, 'Invalid variable type p_filelist'); 187 $v_result = AK_ZIP_ERR_INVALID_PARAMETER; 188 } 189 if ($v_result != 1) { 190 return 0; 191 } 192 return $p_result_list; 193 } 194 // }}} 195 // {{{ add() 196 197 /** 198 * This method add files or directory in an existing Zip Archive. 199 * If the Zip Archive does not exist it is created. 200 * The files and directories to add are indicated in $p_filelist. 201 * When a directory is in the list, the directory and its content is added 202 * in the archive. 203 * The methods takes a variable list of parameters in $p_params. 204 * The supported parameters for this method are : 205 * 'add_path' : Add a path to the archived files. 206 * 'remove_path' : Remove the specified 'root' path of the archived files. 207 * 'remove_all_path' : Remove all the path of the archived files. 208 * 'no_compression' : The archived files will not be compressed. 209 * 'callback_pre_add' : A callback function that will be called before 210 * each entry archiving. 211 * 'callback_post_add' : A callback function that will be called after 212 * each entry archiving. 213 * 214 * @access public 215 * @param mixed $p_filelist The list of the files or folders to add. 216 * It can be a string with filenames separated 217 * by a comma, or an array of filenames. 218 * @param mixed $p_params An array of variable parameters and values. 219 * @return mixed An array of file description on success, 220 * 0 on an unrecoverable failure, an error code is logged. 221 */ 222 223 function add($p_filelist, $p_params = 0) 224 { 225 $this->_errorReset(); 226 // ----- Set default values 227 if ($p_params === 0) { 228 $p_params = array(); 229 } 230 if ($this->_check_parameters($p_params, array( 231 'no_compression' => false, 232 'add_path' => '', 233 'remove_path' => '', 234 'remove_all_path' => false, 235 'callback_pre_add' => '', 236 'callback_post_add' => '' 237 )) != 1) { 238 return 0; 239 } 240 // ----- Look if the $p_filelist is really an array 241 $p_result_list = array(); 242 if (is_array($p_filelist)) { 243 // ----- Call the create fct 244 $v_result = $this->_add($p_filelist, $p_result_list, $p_params); 245 } 246 // ----- Look if the $p_filelist is a string 247 else if (is_string($p_filelist)) { 248 // ----- Create a list with the elements from the string 249 $v_list = explode(AK_ZIP_SEPARATOR, $p_filelist); 250 // ----- Call the create fct 251 $v_result = $this->_add($v_list, $p_result_list, $p_params); 252 } 253 // ----- Invalid variable 254 else { 255 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, "add() : Invalid variable type p_filelist"); 256 $v_result = AK_ZIP_ERR_INVALID_PARAMETER; 257 } 258 if ($v_result != 1) { 259 return 0; 260 } 261 // ----- Return the result list 262 return $p_result_list; 263 } 264 // }}} 265 // {{{ listContent() 266 267 /** 268 * This method gives the names and properties of the files and directories 269 * which are present in the zip archive. 270 * The properties of each entries in the list are : 271 * filename : Name of the file. 272 * For create() or add() it's the filename given by the user. 273 * For an extract() it's the filename of the extracted file. 274 * stored_filename : Name of the file / directory stored in the archive. 275 * size : Size of the stored file. 276 * compressed_size : Size of the file's data compressed in the archive 277 * (without the zip headers overhead) 278 * mtime : Last known modification date of the file (UNIX timestamp) 279 * comment : Comment associated with the file 280 * folder : true | false (indicates if the entry is a folder) 281 * index : index of the file in the archive (-1 when not available) 282 * status : status of the action on the entry (depending of the action) : 283 * Values are : 284 * ok : OK ! 285 * filtered : the file/dir was not extracted (filtered by user) 286 * already_a_directory : the file can't be extracted because a 287 * directory with the same name already 288 * exists 289 * write_protected : the file can't be extracted because a file 290 * with the same name already exists and is 291 * write protected 292 * newer_exist : the file was not extracted because a newer 293 * file already exists 294 * path_creation_fail : the file is not extracted because the 295 * folder does not exists and can't be 296 * created 297 * write_error : the file was not extracted because there was a 298 * error while writing the file 299 * read_error : the file was not extracted because there was a 300 * error while reading the file 301 * invalid_header : the file was not extracted because of an 302 * archive format error (bad file header) 303 * Note that each time a method can continue operating when there 304 * is an error on a single file, the error is only logged in the file status. 305 * 306 * @access public 307 * @return mixed An array of file description on success, 308 * 0 on an unrecoverable failure, an error code is logged. 309 */ 310 311 function listContent() 312 { 313 $this->_errorReset(); 314 // ----- Check archive 315 if (!$this->_checkFormat()) { 316 return (0); 317 } 318 $v_list = array(); 319 if ($this->_list($v_list) != 1) { 320 unset($v_list); 321 return (0); 322 } 323 return $v_list; 324 } 325 // }}} 326 // {{{ extract() 327 328 /** 329 * This method extract the files and folders which are in the zip archive. 330 * It can extract all the archive or a part of the archive by using filter 331 * feature (extract by name, by index, by ereg, by preg). The extraction 332 * can occur in the current path or an other path. 333 * All the advanced features are activated by the use of variable 334 * parameters. 335 * The return value is an array of entry descriptions which gives 336 * information on extracted files (See listContent()). 337 * The method may return a success value (an array) even if some files 338 * are not correctly extracted (see the file status in listContent()). 339 * The supported variable parameters for this method are : 340 * 'add_path' : Path where the files and directories are to be extracted 341 * 'remove_path' : First part ('root' part) of the memorized path 342 * (if similar) to remove while extracting. 343 * 'remove_all_path' : Remove all the memorized path while extracting. 344 * 'extract_as_string' : 345 * 'set_chmod' : After the extraction of the file the indicated mode 346 * will be set. 347 * 'by_name' : It can be a string with file/dir names separated by ',', 348 * or an array of file/dir names to extract from the archive. 349 * 'by_index' : A string with range of indexes separated by ',', 350 * (sample "1,3-5,12"). 351 * 'by_ereg' : A regular expression (ereg) that must match the extracted 352 * filename. 353 * 'by_preg' : A regular expression (preg) that must match the extracted 354 * filename. 355 * 'callback_pre_extract' : A callback function that will be called before 356 * each entry extraction. 357 * 'callback_post_extract' : A callback function that will be called after 358 * each entry extraction. 359 * 360 * @access public 361 * @param mixed $p_params An array of variable parameters and values. 362 * @return mixed An array of file description on success, 363 * 0 on an unrecoverable failure, an error code is logged. 364 */ 365 366 function extract($p_params = 0) 367 { 368 $this->_errorReset(); 369 // ----- Check archive 370 if (!$this->_checkFormat()) { 371 return (0); 372 } 373 // ----- Set default values 374 if ($p_params === 0) { 375 $p_params = array(); 376 } 377 if ($this->_check_parameters($p_params, array( 378 'extract_as_string' => false, 379 'add_path' => '', 380 'remove_path' => '', 381 'remove_all_path' => false, 382 'callback_pre_extract' => '', 383 'callback_post_extract' => '', 384 'set_chmod' => 0, 385 'by_name' => '', 386 'by_index' => '', 387 'by_ereg' => '', 388 'by_preg' => '' 389 )) != 1) { 390 return 0; 391 } 392 // ----- Call the extracting fct 393 $v_list = array(); 394 if ($this->_extractByRule($v_list, $p_params) != 1) { 395 unset($v_list); 396 return (0); 397 } 398 return $v_list; 399 } 400 // }}} 401 // {{{ delete() 402 403 /** 404 * This methods delete archive entries in the zip archive. 405 * Notice that at least one filtering rule (set by the variable parameter 406 * list) must be set. 407 * Also notice that if you delete a folder entry, only the folder entry 408 * is deleted, not all the files bellonging to this folder. 409 * The supported variable parameters for this method are : 410 * 'by_name' : It can be a string with file/dir names separated by ',', 411 * or an array of file/dir names to delete from the archive. 412 * 'by_index' : A string with range of indexes separated by ',', 413 * (sample "1,3-5,12"). 414 * 'by_ereg' : A regular expression (ereg) that must match the extracted 415 * filename. 416 * 'by_preg' : A regular expression (preg) that must match the extracted 417 * filename. 418 * 419 * @access public 420 * @param mixed $p_params An array of variable parameters and values. 421 * @return mixed An array of file description on success, 422 * 0 on an unrecoverable failure, an error code is logged. 423 */ 424 425 function delete($p_params) 426 { 427 $this->_errorReset(); 428 // ----- Check archive 429 if (!$this->_checkFormat()) { 430 return (0); 431 } 432 // ----- Set default values 433 if ($this->_check_parameters($p_params, array( 434 'by_name' => '', 435 'by_index' => '', 436 'by_ereg' => '', 437 'by_preg' => '' 438 )) != 1) { 439 return 0; 440 } 441 // ----- Check that at least one rule is set 442 if (($p_params['by_name'] == '') && ($p_params['by_index'] == '') && ($p_params['by_ereg'] == '') && ($p_params['by_preg'] == '')) { 443 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, 'At least one filtering rule must'.' be set as parameter'); 444 return 0; 445 } 446 // ----- Call the delete fct 447 $v_list = array(); 448 if ($this->_deleteByRule($v_list, $p_params) != 1) { 449 unset($v_list); 450 return (0); 451 } 452 return $v_list; 453 } 454 // }}} 455 // {{{ properties() 456 457 /** 458 * This method gives the global properties of the archive. 459 * The properties are : 460 * nb : Number of files in the archive 461 * comment : Comment associated with the archive file 462 * status : not_exist, ok 463 * 464 * @access public 465 * @param mixed $p_params {Description} 466 * @return mixed An array with the global properties or 0 on error. 467 */ 468 469 function properties() 470 { 471 $this->_errorReset(); 472 // ----- Check archive 473 if (!$this->_checkFormat()) { 474 return (0); 475 } 476 // ----- Default properties 477 $v_prop = array(); 478 $v_prop['comment'] = ''; 479 $v_prop['nb'] = 0; 480 $v_prop['status'] = 'not_exist'; 481 // ----- Look if file exists 482 if (@is_file($this->_zipname)) { 483 // ----- Open the zip file 484 if (($this->_zip_fd = @fopen($this->_zipname, 'rb')) == 0) { 485 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->_zipname.'\' in binary read mode'); 486 return 0; 487 } 488 // ----- Read the central directory informations 489 $v_central_dir = array(); 490 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) { 491 return 0; 492 } 493 $this->_closeFd(); 494 // ----- Set the user attributes 495 $v_prop['comment'] = $v_central_dir['comment']; 496 $v_prop['nb'] = $v_central_dir['entries']; 497 $v_prop['status'] = 'ok'; 498 } 499 return $v_prop; 500 } 501 // }}} 502 // {{{ duplicate() 503 504 /** 505 * This method creates an archive by copying the content of an other one. 506 * If the archive already exist, it is replaced by the new one without 507 * any warning. 508 * 509 * @access public 510 * @param mixed $p_archive It can be a valid AkZip object or 511 * the filename of a valid zip archive. 512 * @return integer 1 on success, 0 on failure. 513 */ 514 515 function duplicate($p_archive) 516 { 517 $this->_errorReset(); 518 // ----- Look if the $p_archive is a AkZip object 519 if ((is_object($p_archive)) && (strtolower(get_class($p_archive)) == 'archive_zip')) { 520 $v_result = $this->_duplicate($p_archive->_zipname); 521 } 522 // ----- Look if the $p_archive is a string (so a filename) 523 else if (is_string($p_archive)) { 524 // ----- Check that $p_archive is a valid zip file 525 // TBC : Should also check the archive format 526 if (!is_file($p_archive)) { 527 $this->_errorLog(AK_ZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); 528 $v_result = AK_ZIP_ERR_MISSING_FILE; 529 } else { 530 $v_result = $this->_duplicate($p_archive); 531 } 532 } 533 // ----- Invalid variable 534 else { 535 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); 536 $v_result = AK_ZIP_ERR_INVALID_PARAMETER; 537 } 538 return $v_result; 539 } 540 // }}} 541 // {{{ merge() 542 543 /** 544 * This method merge a valid zip archive at the end of the 545 * archive identified by the AkZip object. 546 * If the archive ($this) does not exist, the merge becomes a duplicate. 547 * If the archive to add does not exist, the merge is a success. 548 * 549 * @access public 550 * @param mixed $p_archive_to_add It can be a valid AkZip object or 551 * the filename of a valid zip archive. 552 * @return integer 1 on success, 0 on failure. 553 */ 554 555 function merge($p_archive_to_add) 556 { 557 $v_result = 1; 558 $this->_errorReset(); 559 // ----- Check archive 560 if (!$this->_checkFormat()) { 561 return (0); 562 } 563 // ----- Look if the $p_archive_to_add is a AkZip object 564 if ((is_object($p_archive_to_add)) && (strtolower(get_class($p_archive_to_add)) == 'archive_zip')) { 565 $v_result = $this->_merge($p_archive_to_add); 566 } 567 // ----- Look if the $p_archive_to_add is a string (so a filename) 568 else if (is_string($p_archive_to_add)) { 569 // ----- Create a temporary archive 570 $v_object_archive = new AkZip($p_archive_to_add); 571 // ----- Merge the archive 572 $v_result = $this->_merge($v_object_archive); 573 } 574 // ----- Invalid variable 575 else { 576 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); 577 $v_result = AK_ZIP_ERR_INVALID_PARAMETER; 578 } 579 return $v_result; 580 } 581 // }}} 582 // {{{ errorCode() 583 584 /** 585 * Method that gives the lastest error code. 586 * 587 * @access public 588 * @return integer The error code value. 589 */ 590 591 function errorCode() 592 { 593 return ($this->_error_code); 594 } 595 // }}} 596 // {{{ errorName() 597 598 /** 599 * This method gives the latest error code name. 600 * 601 * @access public 602 * @param boolean $p_with_code If true, gives the name and the int value. 603 * @return string The error name. 604 */ 605 606 function errorName($p_with_code = false) 607 { 608 $v_const_list = get_defined_constants(); 609 // ----- Extract error constants from all const. 610 for (reset($v_const_list) ; list($v_key, $v_value) = each($v_const_list) ;) { 611 if (substr($v_key, 0, strlen('AK_ZIP_ERR_')) == 'AK_ZIP_ERR_') { 612 $v_error_list[$v_key] = $v_value; 613 } 614 } 615 // ----- Search the name form the code value 616 $v_key = array_search($this->_error_code, $v_error_list, true); 617 if ($v_key != false) { 618 $v_value = $v_key; 619 } else { 620 $v_value = 'NoName'; 621 } 622 if ($p_with_code) { 623 return ($v_value.' ('.$this->_error_code.')'); 624 } else { 625 return ($v_value); 626 } 627 } 628 // }}} 629 // {{{ errorInfo() 630 631 /** 632 * This method returns the description associated with the latest error. 633 * 634 * @access public 635 * @param boolean $p_full If set to true gives the description with the 636 * error code, the name and the description. 637 * If set to false gives only the description 638 * and the error code. 639 * @return string The error description. 640 */ 641 642 function errorInfo($p_full = false) 643 { 644 if ($p_full) { 645 return ($this->errorName(true) ." : ".$this->_error_string); 646 } else { 647 return ($this->_error_string." [code ".$this->_error_code."]"); 648 } 649 } 650 // }}} 651 // ----------------------------------------------------------------------------- 652 // ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** 653 // ***** ***** 654 // ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** 655 // ----------------------------------------------------------------------------- 656 // --------------------------------------------------------------------------- 657 // Function : _checkFormat() 658 // Description : 659 // This method check that the archive exists and is a valid zip archive. 660 // Several level of check exists. (futur) 661 // Parameters : 662 // $p_level : Level of check. Default 0. 663 // 0 : Check the first bytes (magic codes) (default value)) 664 // 1 : 0 + Check the central directory (futur) 665 // 2 : 1 + Check each file header (futur) 666 // Return Values : 667 // true on success, 668 // false on error, the error code is set. 669 // --------------------------------------------------------------------------- 670 671 /** 672 * AkZip::_checkFormat() 673 * 674 * { Description } 675 * 676 * @param integer $p_level 677 */ 678 679 function _checkFormat($p_level = 0) 680 { 681 $v_result = true; 682 // ----- Reset the error handler 683 $this->_errorReset(); 684 // ----- Look if the file exits 685 if (!is_file($this->_zipname)) { 686 // ----- Error log 687 $this->_errorLog(AK_ZIP_ERR_MISSING_FILE, "Missing archive file '".$this->_zipname."'"); 688 return (false); 689 } 690 // ----- Check that the file is readeable 691 if (!is_readable($this->_zipname)) { 692 // ----- Error log 693 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->_zipname."'"); 694 return (false); 695 } 696 // ----- Check the magic code 697 // TBC 698 // ----- Check the central header 699 // TBC 700 // ----- Check each file header 701 // TBC 702 // ----- Return 703 return $v_result; 704 } 705 // --------------------------------------------------------------------------- 706 // --------------------------------------------------------------------------- 707 // Function : _create() 708 // Description : 709 // Parameters : 710 // Return Values : 711 // --------------------------------------------------------------------------- 712 713 /** 714 * AkZip::_create() 715 * 716 * { Description } 717 * 718 */ 719 720 function _create($p_list, &$p_result_list, &$p_params) 721 { 722 $v_result = 1; 723 $v_list_detail = array(); 724 $p_add_dir = $p_params['add_path']; 725 $p_remove_dir = $p_params['remove_path']; 726 $p_remove_all_dir = $p_params['remove_all_path']; 727 // ----- Open the file in write mode 728 if (($v_result = $this->_openFd('wb')) != 1) { 729 // ----- Return 730 return $v_result; 731 } 732 // ----- Add the list of files 733 $v_result = $this->_addList($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params); 734 // ----- Close 735 $this->_closeFd(); 736 // ----- Return 737 return $v_result; 738 } 739 // --------------------------------------------------------------------------- 740 // --------------------------------------------------------------------------- 741 // Function : _add() 742 // Description : 743 // Parameters : 744 // Return Values : 745 // --------------------------------------------------------------------------- 746 747 /** 748 * AkZip::_add() 749 * 750 * { Description } 751 * 752 */ 753 754 function _add($p_list, &$p_result_list, &$p_params) 755 { 756 $v_result = 1; 757 $v_list_detail = array(); 758 $p_add_dir = $p_params['add_path']; 759 $p_remove_dir = $p_params['remove_path']; 760 $p_remove_all_dir = $p_params['remove_all_path']; 761 // ----- Look if the archive exists or is empty and need to be created 762 if ((!is_file($this->_zipname)) || (filesize($this->_zipname) == 0)) { 763 $v_result = $this->_create($p_list, $p_result_list, $p_params); 764 return $v_result; 765 } 766 // ----- Open the zip file 767 if (($v_result = $this->_openFd('rb')) != 1) { 768 return $v_result; 769 } 770 // ----- Read the central directory informations 771 $v_central_dir = array(); 772 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) { 773 $this->_closeFd(); 774 return $v_result; 775 } 776 // ----- Go to beginning of File 777 @rewind($this->_zip_fd); 778 // ----- Creates a temporay file 779 $v_zip_temp_name = AK_ZIP_TEMPORARY_DIR.uniqid('archive_zip-') .'.tmp'; 780 // ----- Open the temporary file in write mode 781 if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { 782 $this->_closeFd(); 783 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); 784 return AkZip::errorCode(); 785 } 786 // ----- Copy the files from the archive to the temporary file 787 // TBC : Here I should better append the file and go back to erase the 788 // central dir 789 $v_size = $v_central_dir['offset']; 790 while ($v_size != 0) { 791 $v_read_size = ($v_size < AK_ZIP_READ_BLOCK_SIZE ? $v_size : AK_ZIP_READ_BLOCK_SIZE); 792 $v_buffer = fread($this->_zip_fd, $v_read_size); 793 @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); 794 $v_size-= $v_read_size; 795 } 796 // ----- Swap the file descriptor 797 // Here is a trick : I swap the temporary fd with the zip fd, in order to 798 // use the following methods on the temporary fil and not the real archive 799 $v_swap = $this->_zip_fd; 800 $this->_zip_fd = $v_zip_temp_fd; 801 $v_zip_temp_fd = $v_swap; 802 // ----- Add the files 803 $v_header_list = array(); 804 if (($v_result = $this->_addFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1) { 805 fclose($v_zip_temp_fd); 806 $this->_closeFd(); 807 @unlink($v_zip_temp_name); 808 // ----- Return 809 return $v_result; 810 } 811 // ----- Store the offset of the central dir 812 $v_offset = @ftell($this->_zip_fd); 813 // ----- Copy the block of file headers from the old archive 814 $v_size = $v_central_dir['size']; 815 while ($v_size != 0) { 816 $v_read_size = ($v_size < AK_ZIP_READ_BLOCK_SIZE ? $v_size : AK_ZIP_READ_BLOCK_SIZE); 817 $v_buffer = @fread($v_zip_temp_fd, $v_read_size); 818 @fwrite($this->_zip_fd, $v_buffer, $v_read_size); 819 $v_size-= $v_read_size; 820 } 821 // ----- Create the Central Dir files header 822 for ($i = 0, $v_count = 0 ; $i < sizeof($v_header_list) ; $i++) { 823 // ----- Create the file header 824 if ($v_header_list[$i]['status'] == 'ok') { 825 if (($v_result = $this->_writeCentralFileHeader($v_header_list[$i])) != 1) { 826 fclose($v_zip_temp_fd); 827 $this->_closeFd(); 828 @unlink($v_zip_temp_name); 829 // ----- Return 830 return $v_result; 831 } 832 $v_count++; 833 } 834 // ----- Transform the header to a 'usable' info 835 $this->_convertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); 836 } 837 // ----- Zip file comment 838 $v_comment = ''; 839 // ----- Calculate the size of the central header 840 $v_size = @ftell($this->_zip_fd) -$v_offset; 841 // ----- Create the central dir footer 842 if (($v_result = $this->_writeCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) { 843 // ----- Reset the file list 844 unset($v_header_list); 845 // ----- Return 846 return $v_result; 847 } 848 // ----- Swap back the file descriptor 849 $v_swap = $this->_zip_fd; 850 $this->_zip_fd = $v_zip_temp_fd; 851 $v_zip_temp_fd = $v_swap; 852 // ----- Close 853 $this->_closeFd(); 854 // ----- Close the temporary file 855 @fclose($v_zip_temp_fd); 856 // ----- Delete the zip file 857 // TBC : I should test the result ... 858 @unlink($this->_zipname); 859 // ----- Rename the temporary file 860 // TBC : I should test the result ... 861 //@rename($v_zip_temp_name, $this->_zipname); 862 $this->_tool_Rename($v_zip_temp_name, $this->_zipname); 863 // ----- Return 864 return $v_result; 865 } 866 // --------------------------------------------------------------------------- 867 // --------------------------------------------------------------------------- 868 // Function : _openFd() 869 // Description : 870 // Parameters : 871 // --------------------------------------------------------------------------- 872 873 /** 874 * AkZip::_openFd() 875 * 876 * { Description } 877 * 878 */ 879 880 function _openFd($p_mode) 881 { 882 $v_result = 1; 883 // ----- Look if already open 884 if ($this->_zip_fd != 0) { 885 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->_zipname.'\' already open'); 886 return AkZip::errorCode(); 887 } 888 // ----- Open the zip file 889 if (($this->_zip_fd = @fopen($this->_zipname, $p_mode)) == 0) { 890 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->_zipname.'\' in '.$p_mode.' mode'); 891 return AkZip::errorCode(); 892 } 893 // ----- Return 894 return $v_result; 895 } 896 // --------------------------------------------------------------------------- 897 // --------------------------------------------------------------------------- 898 // Function : _closeFd() 899 // Description : 900 // Parameters : 901 // --------------------------------------------------------------------------- 902 903 /** 904 * AkZip::_closeFd() 905 * 906 * { Description } 907 * 908 */ 909 910 function _closeFd() 911 { 912 $v_result = 1; 913 if ($this->_zip_fd != 0) @fclose($this->_zip_fd); 914 $this->_zip_fd = 0; 915 // ----- Return 916 return $v_result; 917 } 918 // --------------------------------------------------------------------------- 919 // --------------------------------------------------------------------------- 920 // Function : _addList() 921 // Description : 922 // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is 923 // different from the real path of the file. This is usefull if you want to have PclTar 924 // running in any directory, and memorize relative path from an other directory. 925 // Parameters : 926 // $p_list : An array containing the file or directory names to add in the tar 927 // $p_result_list : list of added files with their properties (specially the status field) 928 // $p_add_dir : Path to add in the filename path archived 929 // $p_remove_dir : Path to remove in the filename path archived 930 // Return Values : 931 // --------------------------------------------------------------------------- 932 933 /** 934 * AkZip::_addList() 935 * 936 * { Description } 937 * 938 */ 939 940 function _addList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_params) 941 { 942 $v_result = 1; 943 // ----- Add the files 944 $v_header_list = array(); 945 if (($v_result = $this->_addFileList($p_list, $v_header_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1) { 946 return $v_result; 947 } 948 // ----- Store the offset of the central dir 949 $v_offset = @ftell($this->_zip_fd); 950 // ----- Create the Central Dir files header 951 for ($i = 0, $v_count = 0 ; $i < sizeof($v_header_list) ; $i++) { 952 // ----- Create the file header 953 if ($v_header_list[$i]['status'] == 'ok') { 954 if (($v_result = $this->_writeCentralFileHeader($v_header_list[$i])) != 1) { 955 return $v_result; 956 } 957 $v_count++; 958 } 959 // ----- Transform the header to a 'usable' info 960 $this->_convertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); 961 } 962 // ----- Zip file comment 963 $v_comment = ''; 964 // ----- Calculate the size of the central header 965 $v_size = @ftell($this->_zip_fd) -$v_offset; 966 // ----- Create the central dir footer 967 if (($v_result = $this->_writeCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) { 968 // ----- Reset the file list 969 unset($v_header_list); 970 // ----- Return 971 return $v_result; 972 } 973 // ----- Return 974 return $v_result; 975 } 976 // --------------------------------------------------------------------------- 977 // --------------------------------------------------------------------------- 978 // Function : _addFileList() 979 // Description : 980 // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is 981 // different from the real path of the file. This is usefull if you want to 982 // run the lib in any directory, and memorize relative path from an other directory. 983 // Parameters : 984 // $p_list : An array containing the file or directory names to add in the tar 985 // $p_result_list : list of added files with their properties (specially the status field) 986 // $p_add_dir : Path to add in the filename path archived 987 // $p_remove_dir : Path to remove in the filename path archived 988 // Return Values : 989 // --------------------------------------------------------------------------- 990 991 /** 992 * AkZip::_addFileList() 993 * 994 * { Description } 995 * 996 */ 997 998 function _addFileList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_params) 999 { 1000 $v_result = 1; 1001 $v_header = array(); 1002 // ----- Recuperate the current number of elt in list 1003 $v_nb = sizeof($p_result_list); 1004 // ----- Loop on the files 1005 for ($j = 0 ; ($j < count($p_list)) && ($v_result == 1) ; $j++) { 1006 // ----- Recuperate the filename 1007 $p_filename = $this->_tool_TranslateWinPath($p_list[$j], false); 1008 // ----- Skip empty file names 1009 if ($p_filename == "") { 1010 continue; 1011 } 1012 // ----- Check the filename 1013 if (!file_exists($p_filename)) { 1014 $this->_errorLog(AK_ZIP_ERR_MISSING_FILE, "File '$p_filename' does not exists"); 1015 return AkZip::errorCode(); 1016 } 1017 // ----- Look if it is a file or a dir with no all pathnre move 1018 if ((is_file($p_filename)) || ((is_dir($p_filename)) && !$p_remove_all_dir)) { 1019 // ----- Add the file 1020 if (($v_result = $this->_addFile($p_filename, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1) { 1021 // ----- Return status 1022 return $v_result; 1023 } 1024 // ----- Store the file infos 1025 $p_result_list[$v_nb++] = $v_header; 1026 } 1027 // ----- Look for directory 1028 if (is_dir($p_filename)) { 1029 // ----- Look for path 1030 if ($p_filename != ".") $v_path = $p_filename."/"; 1031 else $v_path = ""; 1032 // ----- Read the directory for files and sub-directories 1033 $p_hdir = opendir($p_filename); 1034 $p_hitem = readdir($p_hdir); // '.' directory 1035 $p_hitem = readdir($p_hdir); // '..' directory 1036 while ($p_hitem = readdir($p_hdir)) { 1037 // ----- Look for a file 1038 if (is_file($v_path.$p_hitem)) { 1039 // ----- Add the file 1040 if (($v_result = $this->_addFile($v_path.$p_hitem, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1) { 1041 // ----- Return status 1042 return $v_result; 1043 } 1044 // ----- Store the file infos 1045 $p_result_list[$v_nb++] = $v_header; 1046 } 1047 // ----- Recursive call to _addFileList() 1048 else { 1049 // ----- Need an array as parameter 1050 $p_temp_list[0] = $v_path.$p_hitem; 1051 $v_result = $this->_addFileList($p_temp_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params); 1052 // ----- Update the number of elements of the list 1053 $v_nb = sizeof($p_result_list); 1054 } 1055 } 1056 // ----- Free memory for the recursive loop 1057 unset($p_temp_list); 1058 unset($p_hdir); 1059 unset($p_hitem); 1060 } 1061 } 1062 return $v_result; 1063 } 1064 // --------------------------------------------------------------------------- 1065 // --------------------------------------------------------------------------- 1066 // Function : _addFile() 1067 // Description : 1068 // Parameters : 1069 // Return Values : 1070 // --------------------------------------------------------------------------- 1071 1072 /** 1073 * AkZip::_addFile() 1074 * 1075 * { Description } 1076 * 1077 */ 1078 1079 function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_params) 1080 { 1081 $v_result = 1; 1082 if ($p_filename == "") { 1083 // ----- Error log 1084 $this->_errorLog(AK_ZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); 1085 // ----- Return 1086 return AkZip::errorCode(); 1087 } 1088 // ----- Calculate the stored filename 1089 $v_stored_filename = $p_filename; 1090 // ----- Look for all path to remove 1091 if ($p_remove_all_dir) { 1092 $v_stored_filename = basename($p_filename); 1093 } 1094 // ----- Look for partial path remove 1095 else if ($p_remove_dir != "") { 1096 if (substr($p_remove_dir, -1) != '/') $p_remove_dir.= "/"; 1097 if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) { 1098 if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) $p_remove_dir = "./".$p_remove_dir; 1099 if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) $p_remove_dir = substr($p_remove_dir, 2); 1100 } 1101 $v_compare = $this->_tool_PathInclusion($p_remove_dir, $p_filename); 1102 if ($v_compare > 0) 1103 // if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) 1104 { 1105 if ($v_compare == 2) { 1106 $v_stored_filename = ""; 1107 } else { 1108 $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); 1109 } 1110 } 1111 } 1112 // ----- Look for path to add 1113 if ($p_add_dir != "") { 1114 if (substr($p_add_dir, -1) == "/") $v_stored_filename = $p_add_dir.$v_stored_filename; 1115 else $v_stored_filename = $p_add_dir."/".$v_stored_filename; 1116 } 1117 // ----- Filename (reduce the path of stored name) 1118 $v_stored_filename = $this->_tool_PathReduction($v_stored_filename); 1119 /* filename length moved after call-back in release 1.3 1120 // ----- Check the path length 1121 if (strlen($v_stored_filename) > 0xFF) 1122 { 1123 // ----- Error log 1124 $this->_errorLog(-5, "Stored file name is too long (max. 255) : '$v_stored_filename'"); 1125 1126 // ----- Return 1127 return AkZip::errorCode(); 1128 } 1129 */ 1130 // ----- Set the file properties 1131 clearstatcache(); 1132 $p_header['version'] = 20; 1133 $p_header['version_extracted'] = 10; 1134 $p_header['flag'] = 0; 1135 $p_header['compression'] = 0; 1136 $p_header['mtime'] = filemtime($p_filename); 1137 $p_header['crc'] = 0; 1138 $p_header['compressed_size'] = 0; 1139 $p_header['size'] = filesize($p_filename); 1140 $p_header['filename_len'] = strlen($p_filename); 1141 $p_header['extra_len'] = 0; 1142 $p_header['comment_len'] = 0; 1143 $p_header['disk'] = 0; 1144 $p_header['internal'] = 0; 1145 $p_header['external'] = (is_file($p_filename) ? 0xFE49FFE0 : 0x41FF0010); 1146 $p_header['offset'] = 0; 1147 $p_header['filename'] = $p_filename; 1148 $p_header['stored_filename'] = $v_stored_filename; 1149 $p_header['extra'] = ''; 1150 $p_header['comment'] = ''; 1151 $p_header['status'] = 'ok'; 1152 $p_header['index'] = -1; 1153 // ----- Look for pre-add callback 1154 if ((isset($p_params[AK_ZIP_PARAM_PRE_ADD])) && ($p_params[AK_ZIP_PARAM_PRE_ADD] != '')) { 1155 // ----- Generate a local information 1156 $v_local_header = array(); 1157 $this->_convertHeader2FileInfo($p_header, $v_local_header); 1158 // ----- Call the callback 1159 // Here I do not use call_user_func() because I need to send a reference to the 1160 // header. 1161 eval('$v_result = '.$p_params[AK_ZIP_PARAM_PRE_ADD].'(AK_ZIP_PARAM_PRE_ADD, $v_local_header);'); 1162 if ($v_result == 0) { 1163 // ----- Change the file status 1164 $p_header['status'] = "skipped"; 1165 $v_result = 1; 1166 } 1167 // ----- Update the informations 1168 // Only some fields can be modified 1169 if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { 1170 $p_header['stored_filename'] = $this->_tool_PathReduction($v_local_header['stored_filename']); 1171 } 1172 } 1173 // ----- Look for empty stored filename 1174 if ($p_header['stored_filename'] == "") { 1175 $p_header['status'] = "filtered"; 1176 } 1177 // ----- Check the path length 1178 if (strlen($p_header['stored_filename']) > 0xFF) { 1179 $p_header['status'] = 'filename_too_long'; 1180 } 1181 // ----- Look if no error, or file not skipped 1182 if ($p_header['status'] == 'ok') { 1183 // ----- Look for a file 1184 if (is_file($p_filename)) { 1185 // ----- Open the source file 1186 if (($v_file = @fopen($p_filename, "rb")) == 0) { 1187 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); 1188 return AkZip::errorCode(); 1189 } 1190 if ($p_params['no_compression']) { 1191 // ----- Read the file content 1192 $v_content_compressed = @fread($v_file, $p_header['size']); 1193 // ----- Calculate the CRC 1194 $p_header['crc'] = crc32($v_content_compressed); 1195 } else { 1196 // ----- Read the file content 1197 $v_content = @fread($v_file, $p_header['size']); 1198 // ----- Calculate the CRC 1199 $p_header['crc'] = crc32($v_content); 1200 // ----- Compress the file 1201 $v_content_compressed = gzdeflate($v_content); 1202 } 1203 // ----- Set header parameters 1204 $p_header['compressed_size'] = strlen($v_content_compressed); 1205 $p_header['compression'] = 8; 1206 // ----- Call the header generation 1207 if (($v_result = $this->_writeFileHeader($p_header)) != 1) { 1208 @fclose($v_file); 1209 return $v_result; 1210 } 1211 // ----- Write the compressed content 1212 $v_binary_data = pack('a'.$p_header['compressed_size'], $v_content_compressed); 1213 @fwrite($this->_zip_fd, $v_binary_data, $p_header['compressed_size']); 1214 // ----- Close the file 1215 @fclose($v_file); 1216 } 1217 // ----- Look for a directory 1218 else { 1219 // ----- Set the file properties 1220 $p_header['filename'].= '/'; 1221 $p_header['filename_len']++; 1222 $p_header['size'] = 0; 1223 $p_header['external'] = 0x41FF0010; // Value for a folder : to be checked 1224 // ----- Call the header generation 1225 if (($v_result = $this->_writeFileHeader($p_header)) != 1) { 1226 return $v_result; 1227 } 1228 } 1229 } 1230 // ----- Look for pre-add callback 1231 if ((isset($p_params[AK_ZIP_PARAM_POST_ADD])) && ($p_params[AK_ZIP_PARAM_POST_ADD] != '')) { 1232 // ----- Generate a local information 1233 $v_local_header = array(); 1234 $this->_convertHeader2FileInfo($p_header, $v_local_header); 1235 // ----- Call the callback 1236 // Here I do not use call_user_func() because I need to send a reference to the 1237 // header. 1238 eval('$v_result = '.$p_params[AK_ZIP_PARAM_POST_ADD].'(AK_ZIP_PARAM_POST_ADD, $v_local_header);'); 1239 if ($v_result == 0) { 1240 // ----- Ignored 1241 $v_result = 1; 1242 } 1243 // ----- Update the informations 1244 // Nothing can be modified 1245 1246 } 1247 // ----- Return 1248 return $v_result; 1249 } 1250 // --------------------------------------------------------------------------- 1251 // --------------------------------------------------------------------------- 1252 // Function : _writeFileHeader() 1253 // Description : 1254 // Parameters : 1255 // Return Values : 1256 // --------------------------------------------------------------------------- 1257 1258 /** 1259 * AkZip::_writeFileHeader() 1260 * 1261 * { Description } 1262 * 1263 */ 1264 1265 function _writeFileHeader(&$p_header) 1266 { 1267 $v_result = 1; 1268 // TBC 1269 //for(reset($p_header); $key = key($p_header); next($p_header)) { 1270 //} 1271 // ----- Store the offset position of the file 1272 $p_header['offset'] = ftell($this->_zip_fd); 1273 // ----- Transform UNIX mtime to DOS format mdate/mtime 1274 $v_date = getdate($p_header['mtime']); 1275 $v_mtime = ($v_date['hours']<<11) +($v_date['minutes']<<5) +$v_date['seconds']/2; 1276 $v_mdate = (($v_date['year']-1980) <<9) +($v_date['mon']<<5) +$v_date['mday']; 1277 // ----- Packed data 1278 $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']) , $p_header['extra_len']); 1279 // ----- Write the first 148 bytes of the header in the archive 1280 fputs($this->_zip_fd, $v_binary_data, 30); 1281 // ----- Write the variable fields 1282 if (strlen($p_header['stored_filename']) != 0) { 1283 fputs($this->_zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); 1284 } 1285 if ($p_header['extra_len'] != 0) { 1286 fputs($this->_zip_fd, $p_header['extra'], $p_header['extra_len']); 1287 } 1288 // ----- Return 1289 return $v_result; 1290 } 1291 // --------------------------------------------------------------------------- 1292 // --------------------------------------------------------------------------- 1293 // Function : _writeCentralFileHeader() 1294 // Description : 1295 // Parameters : 1296 // Return Values : 1297 // --------------------------------------------------------------------------- 1298 1299 /** 1300 * AkZip::_writeCentralFileHeader() 1301 * 1302 * { Description } 1303 * 1304 */ 1305 1306 function _writeCentralFileHeader(&$p_header) 1307 { 1308 $v_result = 1; 1309 // TBC 1310 //for(reset($p_header); $key = key($p_header); next($p_header)) { 1311 //} 1312 // ----- Transform UNIX mtime to DOS format mdate/mtime 1313 $v_date = getdate($p_header['mtime']); 1314 $v_mtime = ($v_date['hours']<<11) +($v_date['minutes']<<5) +$v_date['seconds']/2; 1315 $v_mdate = (($v_date['year']-1980) <<9) +($v_date['mon']<<5) +$v_date['mday']; 1316 // ----- Packed data 1317 $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']) , $p_header['extra_len'], $p_header['comment_len'], $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); 1318 // ----- Write the 42 bytes of the header in the zip file 1319 fputs($this->_zip_fd, $v_binary_data, 46); 1320 // ----- Write the variable fields 1321 if (strlen($p_header['stored_filename']) != 0) { 1322 fputs($this->_zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); 1323 } 1324 if ($p_header['extra_len'] != 0) { 1325 fputs($this->_zip_fd, $p_header['extra'], $p_header['extra_len']); 1326 } 1327 if ($p_header['comment_len'] != 0) { 1328 fputs($this->_zip_fd, $p_header['comment'], $p_header['comment_len']); 1329 } 1330 // ----- Return 1331 return $v_result; 1332 } 1333 // --------------------------------------------------------------------------- 1334 // --------------------------------------------------------------------------- 1335 // Function : _writeCentralHeader() 1336 // Description : 1337 // Parameters : 1338 // Return Values : 1339 // --------------------------------------------------------------------------- 1340 1341 /** 1342 * AkZip::_writeCentralHeader() 1343 * 1344 * { Description } 1345 * 1346 */ 1347 1348 function _writeCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) 1349 { 1350 $v_result = 1; 1351 // ----- Packed data 1352 $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); 1353 // ----- Write the 22 bytes of the header in the zip file 1354 fputs($this->_zip_fd, $v_binary_data, 22); 1355 // ----- Write the variable fields 1356 if (strlen($p_comment) != 0) { 1357 fputs($this->_zip_fd, $p_comment, strlen($p_comment)); 1358 } 1359 // ----- Return 1360 return $v_result; 1361 } 1362 // --------------------------------------------------------------------------- 1363 // --------------------------------------------------------------------------- 1364 // Function : _list() 1365 // Description : 1366 // Parameters : 1367 // Return Values : 1368 // --------------------------------------------------------------------------- 1369 1370 /** 1371 * AkZip::_list() 1372 * 1373 * { Description } 1374 * 1375 */ 1376 1377 function _list(&$p_list) 1378 { 1379 $v_result = 1; 1380 // ----- Open the zip file 1381 if (($this->_zip_fd = @fopen($this->_zipname, 'rb')) == 0) { 1382 // ----- Error log 1383 $this->_errorLog(AK_ZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->_zipname.'\' in binary read mode'); 1384 // ----- Return 1385 return AkZip::errorCode(); 1386 } 1387 // ----- Read the central directory informations 1388 $v_central_dir = array(); 1389 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) { 1390 return $v_result; 1391 } 1392 // ----- Go to beginning of Central Dir 1393 @rewind($this->_zip_fd); 1394 if (@fseek($this->_zip_fd, $v_central_dir['offset'])) { 1395 // ----- Error log 1396 $this->_errorLog(AK_ZIP_ERR_INVALID_AK_ZIP, 'Invalid archive size'); 1397 // ----- Return 1398 return AkZip::errorCode(); 1399 } 1400 // ----- Read each entry 1401 for ($i = 0 ; $i < $v_central_dir['entries'] ; $i++) { 1402 // ----- Read the file header 1403 if (($v_result = $this->_readCentralFileHeader($v_header)) != 1) { 1404 return $v_result; 1405 } 1406 $v_header['index'] = $i; 1407 // ----- Get the only interesting attributes 1408 $this->_convertHeader2FileInfo($v_header, $p_list[$i]); 1409 unset($v_header); 1410 } 1411 // ----- Close the zip file 1412 $this->_closeFd(); 1413 // ----- Return 1414 return $v_result; 1415 } 1416 // --------------------------------------------------------------------------- 1417 // --------------------------------------------------------------------------- 1418 // Function : _convertHeader2FileInfo() 1419 // Description : 1420 // This function takes the file informations from the central directory 1421 // entries and extract the interesting parameters that will be given back. 1422 // The resulting file infos are set in the array $p_info 1423 // $p_info['filename'] : Filename with full path. Given by user (add), 1424 // extracted in the filesystem (extract). 1425 // $p_info['stored_filename'] : Stored filename in the archive. 1426 // $p_info['size'] = Size of the file. 1427 // $p_info['compressed_size'] = Compressed size of the file. 1428 // $p_info['mtime'] = Last modification date of the file. 1429 // $p_info['comment'] = Comment associated with the file. 1430 // $p_info['folder'] = true/false : indicates if the entry is a folder or not. 1431 // $p_info['status'] = status of the action on the file. 1432 // Parameters : 1433 // Return Values : 1434 // --------------------------------------------------------------------------- 1435 1436 /** 1437 * AkZip::_convertHeader2FileInfo() 1438 * 1439 * { Description } 1440 * 1441 */ 1442 1443 function _convertHeader2FileInfo($p_header, &$p_info) 1444 { 1445 $v_result = 1; 1446 // ----- Get the interesting attributes 1447 $p_info['filename'] = $p_header['filename']; 1448 $p_info['stored_filename'] = $p_header['stored_filename']; 1449 $p_info['size'] = $p_header['size']; 1450 $p_info['compressed_size'] = $p_header['compressed_size']; 1451 $p_info['mtime'] = $p_header['mtime']; 1452 $p_info['comment'] = $p_header['comment']; 1453 $p_info['folder'] = (($p_header['external']&0x00000010) == 0x00000010); 1454 $p_info['index'] = $p_header['index']; 1455 $p_info['status'] = $p_header['status']; 1456 // ----- Return 1457 return $v_result; 1458 } 1459 // --------------------------------------------------------------------------- 1460 // --------------------------------------------------------------------------- 1461 // Function : _extractByRule() 1462 // Description : 1463 // Extract a file or directory depending of rules (by index, by name, ...) 1464 // Parameters : 1465 // $p_file_list : An array where will be placed the properties of each 1466 // extracted file 1467 // $p_path : Path to add while writing the extracted files 1468 // $p_remove_path : Path to remove (from the file memorized path) while writing the 1469 // extracted files. If the path does not match the file path, 1470 // the file is extracted with its memorized path. 1471 // $p_remove_path does not apply to 'list' mode. 1472 // $p_path and $p_remove_path are commulative. 1473 // Return Values : 1474 // 1 on success,0 or less on error (see error code list) 1475 // --------------------------------------------------------------------------- 1476 1477 /** 1478 * AkZip::_extractByRule() 1479 * 1480 * { Description } 1481 * 1482 */ 1483 1484 function _extractByRule(&$p_file_list, &$p_params) 1485 { 1486 $v_result = 1; 1487 $p_path = $p_params['add_path']; 1488 $p_remove_path = $p_params['remove_path']; 1489 $p_remove_all_path = $p_params['remove_all_path']; 1490 // ----- Check the path 1491 if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path, 1, 2) != ":/"))) $p_path = "./".$p_path; 1492 // ----- Reduce the path last (and duplicated) '/' 1493 if (($p_path != "./") && ($p_path != "/")) { 1494 // ----- Look for the path end '/' 1495 while (substr($p_path, -1) == "/") { 1496 $p_path = substr($p_path, 0, strlen($p_path) -1); 1497 } 1498 } 1499 // ----- Look for path to remove format (should end by /) 1500 if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) { 1501 $p_remove_path.= '/'; 1502 } 1503 $p_remove_path_size = strlen($p_remove_path); 1504 // ----- Open the zip file 1505 if (($v_result = $this->_openFd('rb')) != 1) { 1506 return $v_result; 1507 } 1508 // ----- Read the central directory informations 1509 $v_central_dir = array(); 1510 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) { 1511 // ----- Close the zip file 1512 $this->_closeFd(); 1513 return $v_result; 1514 } 1515 // ----- Start at beginning of Central Dir 1516 $v_pos_entry = $v_central_dir['offset']; 1517 // ----- Read each entry 1518 $j_start = 0; 1519 for ($i = 0, $v_nb_extracted = 0 ; $i < $v_central_dir['entries'] ; $i++) { 1520 // ----- Read next Central dir entry 1521 @rewind($this->_zip_fd); 1522 if (@fseek($this->_zip_fd, $v_pos_entry)) { 1523 $this->_closeFd(); 1524 $this->_errorLog(AK_ZIP_ERR_INVALID_AK_ZIP, 'Invalid archive size'); 1525 return AkZip::errorCode(); 1526 } 1527 // ----- Read the file header 1528 $v_header = array(); 1529 if (($v_result = $this->_readCentralFileHeader($v_header)) != 1) { 1530 $this->_closeFd(); 1531 return $v_result; 1532 } 1533 // ----- Store the index 1534 $v_header['index'] = $i; 1535 // ----- Store the file position 1536 $v_pos_entry = ftell($this->_zip_fd); 1537 // ----- Look for the specific extract rules 1538 $v_extract = false; 1539 // ----- Look for extract by name rule 1540 if ((isset($p_params[AK_ZIP_PARAM_BY_NAME])) && ($p_params[AK_ZIP_PARAM_BY_NAME] != 0)) { 1541 // ----- Look if the filename is in the list 1542 for ($j = 0 ; ($j < sizeof($p_params[AK_ZIP_PARAM_BY_NAME])) && (!$v_extract) ; $j++) { 1543 // ----- Look for a directory 1544 if (substr($p_params[AK_ZIP_PARAM_BY_NAME][$j], -1) == "/") { 1545 // ----- Look if the directory is in the filename path 1546 if ((strlen($v_header['stored_filename']) > strlen($p_params[AK_ZIP_PARAM_BY_NAME][$j])) && (substr($v_header['stored_filename'], 0, strlen($p_params[AK_ZIP_PARAM_BY_NAME][$j])) == $p_params[AK_ZIP_PARAM_BY_NAME][$j])) { 1547 $v_extract = true; 1548 } 1549 } 1550 // ----- Look for a filename 1551 elseif ($v_header['stored_filename'] == $p_params[AK_ZIP_PARAM_BY_NAME][$j]) { 1552 $v_extract = true; 1553 } 1554 } 1555 } 1556 // ----- Look for extract by ereg rule 1557 else if ((isset($p_params[AK_ZIP_PARAM_BY_EREG])) && ($p_params[AK_ZIP_PARAM_BY_EREG] != "")) { 1558 if (ereg($p_params[AK_ZIP_PARAM_BY_EREG], $v_header['stored_filename'])) { 1559 $v_extract = true; 1560 } 1561 } 1562 // ----- Look for extract by preg rule 1563 else if ((isset($p_params[AK_ZIP_PARAM_BY_PREG])) && ($p_params[AK_ZIP_PARAM_BY_PREG] != "")) { 1564 if (preg_match($p_params[AK_ZIP_PARAM_BY_PREG], $v_header['stored_filename'])) { 1565 $v_extract = true; 1566 } 1567 } 1568 // ----- Look for extract by index rule 1569 else if ((isset($p_params[AK_ZIP_PARAM_BY_INDEX])) && ($p_params[AK_ZIP_PARAM_BY_INDEX] != 0)) { 1570 // ----- Look if the index is in the list 1571 for ($j = $j_start ; ($j < sizeof($p_params[AK_ZIP_PARAM_BY_INDEX])) && (!$v_extract) ; $j++) { 1572 if (($i >= $p_params[AK_ZIP_PARAM_BY_INDEX][$j]['start']) && ($i <= $p_params[AK_ZIP_PARAM_BY_INDEX][$j]['end'])) { 1573 $v_extract = true; 1574 } 1575 if ($i >= $p_params[AK_ZIP_PARAM_BY_INDEX][$j]['end']) { 1576 $j_start = $j+1; 1577 } 1578 if ($p_params[AK_ZIP_PARAM_BY_INDEX][$j]['start'] > $i) { 1579 break; 1580 } 1581 } 1582 } 1583 // ----- Look for no rule, which means extract all the archive 1584 else { 1585 $v_extract = true; 1586 } 1587 // ----- Look for real extraction 1588 if ($v_extract) { 1589 // ----- Go to the file position 1590 @rewind($this->_zip_fd); 1591 if (@fseek($this->_zip_fd, $v_header['offset'])) { 1592 // ----- Close the zip file 1593 $this->_closeFd(); 1594 // ----- Error log 1595 $this->_errorLog(AK_ZIP_ERR_INVALID_AK_ZIP, 'Invalid archive size'); 1596 // ----- Return 1597 return AkZip::errorCode(); 1598 } 1599 // ----- Look for extraction as string 1600 if ($p_params[AK_ZIP_PARAM_EXTRACT_AS_STRING]) { 1601 // ----- Extracting the file 1602 if (($v_result = $this->_extractFileAsString($v_header, $v_string)) != 1) { 1603 // ----- Close the zip file 1604 $this->_closeFd(); 1605 return $v_result; 1606 } 1607 // ----- Get the only interesting attributes 1608 if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) { 1609 // ----- Close the zip file 1610 $this->_closeFd(); 1611 return $v_result; 1612 } 1613 // ----- Set the file content 1614 $p_file_list[$v_nb_extracted]['content'] = $v_string; 1615 // ----- Next extracted file 1616 $v_nb_extracted++; 1617 } else { 1618 // ----- Extracting the file 1619 if (($v_result = $this->_extractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_params)) != 1) { 1620 // ----- Close the zip file 1621 $this->_closeFd(); 1622 return $v_result; 1623 } 1624 // ----- Get the only interesting attributes 1625 if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { 1626 // ----- Close the zip file 1627 $this->_closeFd(); 1628 return $v_result; 1629 } 1630 } 1631 } 1632 } 1633 // ----- Close the zip file 1634 $this->_closeFd(); 1635 // ----- Return 1636 return $v_result; 1637 } 1638 // --------------------------------------------------------------------------- 1639 // --------------------------------------------------------------------------- 1640 // Function : _extractFile() 1641 // Description : 1642 // Parameters : 1643 // Return Values : 1644 // --------------------------------------------------------------------------- 1645 1646 /** 1647 * AkZip::_extractFile() 1648 * 1649 * { Description } 1650 * 1651 */ 1652 1653 function _extractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_params) 1654 { 1655 $v_result = 1; 1656 // ----- Read the file header 1657 if (($v_result = $this->_readFileHeader($v_header)) != 1) { 1658 // ----- Return 1659 return $v_result; 1660 } 1661 // ----- Check that the file header is coherent with $p_entry info 1662 // TBC 1663 // ----- Look for all path to remove 1664 if ($p_remove_all_path == true) { 1665 // ----- Get the basename of the path 1666 $p_entry['filename'] = basename($p_entry['filename']); 1667 } 1668 // ----- Look for path to remove 1669 else if ($p_remove_path != "") { 1670 //if (strcmp($p_remove_path, $p_entry['filename'])==0) 1671 if ($this->_tool_PathInclusion($p_remove_path, $p_entry['filename']) == 2) { 1672 // ----- Change the file status 1673 $p_entry['status'] = "filtered"; 1674 // ----- Return 1675 return $v_result; 1676 } 1677 $p_remove_path_size = strlen($p_remove_path); 1678 if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) { 1679 // ----- Remove the path 1680 $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); 1681 } 1682 } 1683 // ----- Add the path 1684 if ($p_path != '') { 1685 $p_entry['filename'] = $p_path."/".$p_entry['filename']; 1686 } 1687 // ----- Look for pre-extract callback 1688 if ((isset($p_params[AK_ZIP_PARAM_PRE_EXTRACT])) && ($p_params[AK_ZIP_PARAM_PRE_EXTRACT] != '')) { 1689 // ----- Generate a local information 1690 $v_local_header = array(); 1691 $this->_convertHeader2FileInfo($p_entry, $v_local_header); 1692 // ----- Call the callback 1693 // Here I do not use call_user_func() because I need to send a reference to the 1694 // header. 1695 eval('$v_result = '.$p_params[AK_ZIP_PARAM_PRE_EXTRACT].'(AK_ZIP_PARAM_PRE_EXTRACT, $v_local_header);'); 1696 if ($v_result == 0) { 1697 // ----- Change the file status 1698 $p_entry['status'] = "skipped"; 1699 $v_result = 1; 1700 } 1701 // ----- Update the informations 1702 // Only some fields can be modified 1703 $p_entry['filename'] = $v_local_header['filename']; 1704 } 1705 // ----- Trace 1706 // ----- Look if extraction should be done 1707 if ($p_entry['status'] == 'ok') { 1708 // ----- Look for specific actions while the file exist 1709 if (file_exists($p_entry['filename'])) { 1710 // ----- Look if file is a directory 1711 if (is_dir($p_entry['filename'])) { 1712 // ----- Change the file status 1713 $p_entry['status'] = "already_a_directory"; 1714 // ----- Return 1715 //return $v_result; 1716 1717 } 1718 // ----- Look if file is write protected 1719 else if (!is_writeable($p_entry['filename'])) { 1720 // ----- Change the file status 1721 $p_entry['status'] = "write_protected"; 1722 // ----- Return 1723 //return $v_result; 1724 1725 } 1726 // ----- Look if the extracted file is older 1727 else if (filemtime($p_entry['filename']) > $p_entry['mtime']) { 1728 // ----- Change the file status 1729 $p_entry['status'] = "newer_exist"; 1730 // ----- Return 1731 //return $v_result; 1732 1733 } 1734 } 1735 // ----- Check the directory availability and create it if necessary 1736 else { 1737 if ((($p_entry['external']&0x00000010) == 0x00000010) || (substr($p_entry['filename'], -1) == '/')) $v_dir_to_check = $p_entry['filename']; 1738 else if (!strstr($p_entry['filename'], "/")) $v_dir_to_check = ""; 1739 else $v_dir_to_check = dirname($p_entry['filename']); 1740 if (($v_result = $this->_dirCheck($v_dir_to_check, (($p_entry['external']&0x00000010) == 0x00000010))) != 1) { 1741 // ----- Change the file status 1742 $p_entry['status'] = "path_creation_fail"; 1743 // ----- Return 1744 //return $v_result; 1745 $v_result = 1; 1746 } 1747 } 1748 } 1749 // ----- Look if extraction should be done 1750 if ($p_entry['status'] == 'ok') { 1751 // ----- Do the extraction (if not a folder) 1752 if (!(($p_entry['external']&0x00000010) == 0x00000010)) { 1753 // ----- Look for not compressed file 1754 if ($p_entry['compressed_size'] == $p_entry['size']) { 1755 // ----- Opening destination file 1756 if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { 1757 // ----- Change the file status 1758 $p_entry['status'] = "write_error"; 1759 // ----- Return 1760 return $v_result; 1761 } 1762 // ----- Read the file by AK_ZIP_READ_BLOCK_SIZE octets blocks 1763 $v_size = $p_entry['compressed_size']; 1764 while ($v_size != 0) { 1765 $v_read_size = ($v_size < AK_ZIP_READ_BLOCK_SIZE ? $v_size : AK_ZIP_READ_BLOCK_SIZE); 1766 $v_buffer = fread($this->_zip_fd, $v_read_size); 1767 $v_binary_data = pack('a'.$v_read_size, $v_buffer); 1768 @fwrite($v_dest_file, $v_binary_data, $v_read_size); 1769 $v_size-= $v_read_size; 1770 } 1771 // ----- Closing the destination file 1772 fclose($v_dest_file); 1773 // ----- Change the file mtime 1774 touch($p_entry['filename'], $p_entry['mtime']); 1775 } else { 1776 // ----- Trace 1777 // ----- Opening destination file 1778 if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { 1779 // ----- Change the file status 1780 $p_entry['status'] = "write_error"; 1781 return $v_result; 1782 } 1783 // ----- Read the compressed file in a buffer (one shot) 1784 $v_buffer = @fread($this->_zip_fd, $p_entry['compressed_size']); 1785 // ----- Decompress the file 1786 $v_file_content = gzinflate($v_buffer); 1787 unset($v_buffer); 1788 // ----- Write the uncompressed data 1789 @fwrite($v_dest_file, $v_file_content, $p_entry['size']); 1790 unset($v_file_content); 1791 // ----- Closing the destination file 1792 @fclose($v_dest_file); 1793 // ----- Change the file mtime 1794 touch($p_entry['filename'], $p_entry['mtime']); 1795 } 1796 // ----- Look for chmod option 1797 if ((isset($p_params[AK_ZIP_PARAM_SET_CHMOD])) && ($p_params[AK_ZIP_PARAM_SET_CHMOD] != 0)) { 1798 // ----- Change the mode of the file 1799 chmod($p_entry['filename'], $p_params[AK_ZIP_PARAM_SET_CHMOD]); 1800 } 1801 } 1802 } 1803 // ----- Look for post-extract callback 1804 if ((isset($p_params[AK_ZIP_PARAM_POST_EXTRACT])) && ($p_params[AK_ZIP_PARAM_POST_EXTRACT] != '')) { 1805 // ----- Generate a local information 1806 $v_local_header = array(); 1807 $this->_convertHeader2FileInfo($p_entry, $v_local_header); 1808 // ----- Call the callback 1809 // Here I do not use call_user_func() because I need to send a reference to the 1810 // header. 1811 eval('$v_result = '.$p_params[AK_ZIP_PARAM_POST_EXTRACT].'(AK_ZIP_PARAM_POST_EXTRACT, $v_local_header);'); 1812 } 1813 // ----- Return 1814 return $v_result; 1815 } 1816 // --------------------------------------------------------------------------- 1817 // --------------------------------------------------------------------------- 1818 // Function : _extractFileAsString() 1819 // Description : 1820 // Parameters : 1821 // Return Values : 1822 // --------------------------------------------------------------------------- 1823 1824 /** 1825 * AkZip::_extractFileAsString() 1826 * 1827 * { Description } 1828 * 1829 */ 1830 1831 function _extractFileAsString(&$p_entry, &$p_string) 1832 { 1833 $v_result = 1; 1834 // ----- Read the file header 1835 $v_header = array(); 1836 if (($v_result = $this->_readFileHeader($v_header)) != 1) { 1837 // ----- Return 1838 return $v_result; 1839 } 1840 // ----- Check that the file header is coherent with $p_entry info 1841 // TBC 1842 // ----- Trace 1843 // ----- Do the extraction (if not a folder) 1844 if (!(($p_entry['external']&0x00000010) == 0x00000010)) { 1845 // ----- Look for not compressed file 1846 if ($p_entry['compressed_size'] == $p_entry['size']) { 1847 // ----- Trace 1848 // ----- Reading the file 1849 $p_string = fread($this->_zip_fd, $p_entry['compressed_size']); 1850 } else { 1851 // ----- Trace 1852 // ----- Reading the file 1853 $v_data = fread($this->_zip_fd, $p_entry['compressed_size']); 1854 // ----- Decompress the file 1855 $p_string = gzinflate($v_data); 1856 } 1857 // ----- Trace 1858 1859 } else { 1860 // TBC : error : can not extract a folder in a string 1861 1862 } 1863 // ----- Return 1864 return $v_result; 1865 } 1866 // --------------------------------------------------------------------------- 1867 // --------------------------------------------------------------------------- 1868 // Function : _readFileHeader() 1869 // Description : 1870 // Parameters : 1871 // Return Values : 1872 // --------------------------------------------------------------------------- 1873 1874 /** 1875 * AkZip::_readFileHeader() 1876 * 1877 * { Description } 1878 * 1879 */ 1880 1881 function _readFileHeader(&$p_header) 1882 { 1883 $v_result = 1; 1884 // ----- Read the 4 bytes signature 1885 $v_binary_data = @fread($this->_zip_fd, 4); 1886 $v_data = unpack('Vid', $v_binary_data); 1887 // ----- Check signature 1888 if ($v_data['id'] != 0x04034b50) { 1889 // ----- Error log 1890 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); 1891 // ----- Return 1892 return AkZip::errorCode(); 1893 } 1894 // ----- Read the first 42 bytes of the header 1895 $v_binary_data = fread($this->_zip_fd, 26); 1896 // ----- Look for invalid block size 1897 if (strlen($v_binary_data) != 26) { 1898 $p_header['filename'] = ""; 1899 $p_header['status'] = "invalid_header"; 1900 // ----- Error log 1901 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); 1902 // ----- Return 1903 return AkZip::errorCode(); 1904 } 1905 // ----- Extract the values 1906 $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); 1907 // ----- Get filename 1908 $p_header['filename'] = fread($this->_zip_fd, $v_data['filename_len']); 1909 // ----- Get extra_fields 1910 if ($v_data['extra_len'] != 0) { 1911 $p_header['extra'] = fread($this->_zip_fd, $v_data['extra_len']); 1912 } else { 1913 $p_header['extra'] = ''; 1914 } 1915 // ----- Extract properties 1916 $p_header['compression'] = $v_data['compression']; 1917 $p_header['size'] = $v_data['size']; 1918 $p_header['compressed_size'] = $v_data['compressed_size']; 1919 $p_header['crc'] = $v_data['crc']; 1920 $p_header['flag'] = $v_data['flag']; 1921 // ----- Recuperate date in UNIX format 1922 $p_header['mdate'] = $v_data['mdate']; 1923 $p_header['mtime'] = $v_data['mtime']; 1924 if ($p_header['mdate'] && $p_header['mtime']) { 1925 // ----- Extract time 1926 $v_hour = ($p_header['mtime']&0xF800) >>11; 1927 $v_minute = ($p_header['mtime']&0x07E0) >>5; 1928 $v_seconde = ($p_header['mtime']&0x001F) *2; 1929 // ----- Extract date 1930 $v_year = (($p_header['mdate']&0xFE00) >>9) +1980; 1931 $v_month = ($p_header['mdate']&0x01E0) >>5; 1932 $v_day = $p_header['mdate']&0x001F; 1933 // ----- Get UNIX date format 1934 $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); 1935 } else { 1936 $p_header['mtime'] = time(); 1937 } 1938 // ----- Other informations 1939 // TBC 1940 //for(reset($v_data); $key = key($v_data); next($v_data)) { 1941 //} 1942 // ----- Set the stored filename 1943 $p_header['stored_filename'] = $p_header['filename']; 1944 // ----- Set the status field 1945 $p_header['status'] = "ok"; 1946 // ----- Return 1947 return $v_result; 1948 } 1949 // --------------------------------------------------------------------------- 1950 // --------------------------------------------------------------------------- 1951 // Function : _readCentralFileHeader() 1952 // Description : 1953 // Parameters : 1954 // Return Values : 1955 // --------------------------------------------------------------------------- 1956 1957 /** 1958 * AkZip::_readCentralFileHeader() 1959 * 1960 * { Description } 1961 * 1962 */ 1963 1964 function _readCentralFileHeader(&$p_header) 1965 { 1966 $v_result = 1; 1967 // ----- Read the 4 bytes signature 1968 $v_binary_data = @fread($this->_zip_fd, 4); 1969 $v_data = unpack('Vid', $v_binary_data); 1970 // ----- Check signature 1971 if ($v_data['id'] != 0x02014b50) { 1972 // ----- Error log 1973 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); 1974 // ----- Return 1975 return AkZip::errorCode(); 1976 } 1977 // ----- Read the first 42 bytes of the header 1978 $v_binary_data = fread($this->_zip_fd, 42); 1979 // ----- Look for invalid block size 1980 if (strlen($v_binary_data) != 42) { 1981 $p_header['filename'] = ""; 1982 $p_header['status'] = "invalid_header"; 1983 // ----- Error log 1984 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); 1985 // ----- Return 1986 return AkZip::errorCode(); 1987 } 1988 // ----- Extract the values 1989 $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); 1990 // ----- Get filename 1991 if ($p_header['filename_len'] != 0) $p_header['filename'] = fread($this->_zip_fd, $p_header['filename_len']); 1992 else $p_header['filename'] = ''; 1993 // ----- Get extra 1994 if ($p_header['extra_len'] != 0) $p_header['extra'] = fread($this->_zip_fd, $p_header['extra_len']); 1995 else $p_header['extra'] = ''; 1996 // ----- Get comment 1997 if ($p_header['comment_len'] != 0) $p_header['comment'] = fread($this->_zip_fd, $p_header['comment_len']); 1998 else $p_header['comment'] = ''; 1999 // ----- Extract properties 2000 // ----- Recuperate date in UNIX format 2001 if ($p_header['mdate'] && $p_header['mtime']) { 2002 // ----- Extract time 2003 $v_hour = ($p_header['mtime']&0xF800) >>11; 2004 $v_minute = ($p_header['mtime']&0x07E0) >>5; 2005 $v_seconde = ($p_header['mtime']&0x001F) *2; 2006 // ----- Extract date 2007 $v_year = (($p_header['mdate']&0xFE00) >>9) +1980; 2008 $v_month = ($p_header['mdate']&0x01E0) >>5; 2009 $v_day = $p_header['mdate']&0x001F; 2010 // ----- Get UNIX date format 2011 $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); 2012 } else { 2013 $p_header['mtime'] = time(); 2014 } 2015 // ----- Set the stored filename 2016 $p_header['stored_filename'] = $p_header['filename']; 2017 // ----- Set default status to ok 2018 $p_header['status'] = 'ok'; 2019 // ----- Look if it is a directory 2020 if (substr($p_header['filename'], -1) == '/') { 2021 $p_header['external'] = 0x41FF0010; 2022 } 2023 // ----- Return 2024 return $v_result; 2025 } 2026 // --------------------------------------------------------------------------- 2027 // --------------------------------------------------------------------------- 2028 // Function : _readEndCentralDir() 2029 // Description : 2030 // Parameters : 2031 // Return Values : 2032 // --------------------------------------------------------------------------- 2033 2034 /** 2035 * AkZip::_readEndCentralDir() 2036 * 2037 * { Description } 2038 * 2039 */ 2040 2041 function _readEndCentralDir(&$p_central_dir) 2042 { 2043 $v_result = 1; 2044 // ----- Go to the end of the zip file 2045 $v_size = filesize($this->_zipname); 2046 @fseek($this->_zip_fd, $v_size); 2047 if (@ftell($this->_zip_fd) != $v_size) { 2048 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->_zipname.'\''); 2049 return AkZip::errorCode(); 2050 } 2051 // ----- First try : look if this is an archive with no commentaries 2052 // (most of the time) 2053 // in this case the end of central dir is at 22 bytes of the file end 2054 $v_found = 0; 2055 if ($v_size > 26) { 2056 @fseek($this->_zip_fd, $v_size-22); 2057 if (($v_pos = @ftell($this->_zip_fd)) != ($v_size-22)) { 2058 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->_zipname.'\''); 2059 return AkZip::errorCode(); 2060 } 2061 // ----- Read for bytes 2062 $v_binary_data = @fread($this->_zip_fd, 4); 2063 $v_data = unpack('Vid', $v_binary_data); 2064 // ----- Check signature 2065 if ($v_data['id'] == 0x06054b50) { 2066 $v_found = 1; 2067 } 2068 $v_pos = ftell($this->_zip_fd); 2069 } 2070 // ----- Go back to the maximum possible size of the Central Dir End Record 2071 if (!$v_found) { 2072 $v_maximum_size = 65557; // 0xFFFF + 22; 2073 if ($v_maximum_size > $v_size) $v_maximum_size = $v_size; 2074 @fseek($this->_zip_fd, $v_size-$v_maximum_size); 2075 if (@ftell($this->_zip_fd) != ($v_size-$v_maximum_size)) { 2076 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->_zipname.'\''); 2077 return AkZip::errorCode(); 2078 } 2079 // ----- Read byte per byte in order to find the signature 2080 $v_pos = ftell($this->_zip_fd); 2081 $v_bytes = 0x00000000; 2082 while ($v_pos < $v_size) { 2083 // ----- Read a byte 2084 $v_byte = @fread($this->_zip_fd, 1); 2085 // ----- Add the byte 2086 $v_bytes = ($v_bytes<<8) |Ord($v_byte); 2087 // ----- Compare the bytes 2088 if ($v_bytes == 0x504b0506) { 2089 $v_pos++; 2090 break; 2091 } 2092 $v_pos++; 2093 } 2094 // ----- Look if not found end of central dir 2095 if ($v_pos == $v_size) { 2096 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); 2097 return AkZip::errorCode(); 2098 } 2099 } 2100 // ----- Read the first 18 bytes of the header 2101 $v_binary_data = fread($this->_zip_fd, 18); 2102 // ----- Look for invalid block size 2103 if (strlen($v_binary_data) != 18) { 2104 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); 2105 return AkZip::errorCode(); 2106 } 2107 // ----- Extract the values 2108 $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); 2109 // ----- Check the global size 2110 if (($v_pos+$v_data['comment_size']+18) != $v_size) { 2111 $this->_errorLog(AK_ZIP_ERR_BAD_FORMAT, "Fail to find the right signature"); 2112 return AkZip::errorCode(); 2113 } 2114 // ----- Get comment 2115 if ($v_data['comment_size'] != 0) $p_central_dir['comment'] = fread($this->_zip_fd, $v_data['comment_size']); 2116 else $p_central_dir['comment'] = ''; 2117 $p_central_dir['entries'] = $v_data['entries']; 2118 $p_central_dir['disk_entries'] = $v_data['disk_entries']; 2119 $p_central_dir['offset'] = $v_data['offset']; 2120 $p_central_dir['size'] = $v_data['size']; 2121 $p_central_dir['disk'] = $v_data['disk']; 2122 $p_central_dir['disk_start'] = $v_data['disk_start']; 2123 // ----- Return 2124 return $v_result; 2125 } 2126 // --------------------------------------------------------------------------- 2127 // --------------------------------------------------------------------------- 2128 // Function : _deleteByRule() 2129 // Description : 2130 // Parameters : 2131 // Return Values : 2132 // --------------------------------------------------------------------------- 2133 2134 /** 2135 * AkZip::_deleteByRule() 2136 * 2137 * { Description } 2138 * 2139 */ 2140 2141 function _deleteByRule(&$p_result_list, &$p_params) 2142 { 2143 $v_result = 1; 2144 $v_list_detail = array(); 2145 // ----- Open the zip file 2146 if (($v_result = $this->_openFd('rb')) != 1) { 2147 // ----- Return 2148 return $v_result; 2149 } 2150 // ----- Read the central directory informations 2151 $v_central_dir = array(); 2152 if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) { 2153 $this->_closeFd(); 2154 return $v_result; 2155 } 2156 // ----- Go to beginning of File 2157 @rewind($this->_zip_fd); 2158 // ----- Scan all the files 2159 // ----- Start at beginning of Central Dir 2160 $v_pos_entry = $v_central_dir['offset']; 2161 @rewind($this->_zip_fd); 2162 if (@fseek($this->_zip_fd, $v_pos_entry)) { 2163 // ----- Clean 2164 $this->_closeFd(); 2165 $this->_errorLog(AK_ZIP_ERR_INVALID_AK_ZIP, 'Invalid archive size'); 2166 return AkZip::errorCode(); 2167 } 2168 // ----- Read each entry 2169 $v_header_list = array(); 2170 $j_start = 0; 2171 for ($i = 0, $v_nb_extracted = 0 ; $i < $v_central_dir['entries'] ; $i++) { 2172 // ----- Read the file header 2173 $v_header_list[$v_nb_extracted] = array(); 2174 $v_result = $this->_readCentralFileHeader($v_header_list[$v_nb_extracted]); 2175 if ($v_result != 1) { 2176 // ----- Clean 2177 $this->_closeFd(); 2178 return $v_result; 2179 } 2180 // ----- Store the index 2181