[ Index ]

PHP Cross Reference of Akelos Framework

title

Body

[close]

/ -> AkZip.php (source)

   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