Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set ts=4 sw=4: */
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2003 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 3.0 of the PHP license,       |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available through the world-wide-web at the following url:           |
11
// | http://www.php.net/license/3_0.txt.                                  |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Author: Vincent Blavet <vincent@blavet.net>                          |
17
// +----------------------------------------------------------------------+
18
//
19
// $Id: Zip.php 325 2007-12-20 15:44:58Z hans $
20
 
21
  // ----- Constants
22
  define( 'ARCHIVE_ZIP_READ_BLOCK_SIZE', 2048 );
23
 
24
  // ----- File list separator
25
  define( 'ARCHIVE_ZIP_SEPARATOR', ',' );
26
 
27
  // ----- Optional static temporary directory
28
  //       By default temporary files are generated in the script current
29
  //       path.
30
  //       If defined :
31
  //       - MUST BE terminated by a '/'.
32
  //       - MUST be a valid, already created directory
33
  //       Samples :
34
  // define( 'ARCHIVE_ZIP_TEMPORARY_DIR', '/temp/' );
35
  // define( 'ARCHIVE_ZIP_TEMPORARY_DIR', 'C:/Temp/' );
36
  define( 'ARCHIVE_ZIP_TEMPORARY_DIR', '' );
37
 
38
  // ----- Error codes
39
  define( 'ARCHIVE_ZIP_ERR_NO_ERROR', 0 );
40
  define( 'ARCHIVE_ZIP_ERR_WRITE_OPEN_FAIL', -1 );
41
  define( 'ARCHIVE_ZIP_ERR_READ_OPEN_FAIL', -2 );
42
  define( 'ARCHIVE_ZIP_ERR_INVALID_PARAMETER', -3 );
43
  define( 'ARCHIVE_ZIP_ERR_MISSING_FILE', -4 );
44
  define( 'ARCHIVE_ZIP_ERR_FILENAME_TOO_LONG', -5 );
45
  define( 'ARCHIVE_ZIP_ERR_INVALID_ZIP', -6 );
46
  define( 'ARCHIVE_ZIP_ERR_BAD_EXTRACTED_FILE', -7 );
47
  define( 'ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL', -8 );
48
  define( 'ARCHIVE_ZIP_ERR_BAD_EXTENSION', -9 );
49
  define( 'ARCHIVE_ZIP_ERR_BAD_FORMAT', -10 );
50
  define( 'ARCHIVE_ZIP_ERR_DELETE_FILE_FAIL', -11 );
51
  define( 'ARCHIVE_ZIP_ERR_RENAME_FILE_FAIL', -12 );
52
  define( 'ARCHIVE_ZIP_ERR_BAD_CHECKSUM', -13 );
53
  define( 'ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP', -14 );
54
  define( 'ARCHIVE_ZIP_ERR_MISSING_OPTION_VALUE', -15 );
55
  define( 'ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE', -16 );
56
 
57
  // ----- Warning codes
58
  define( 'ARCHIVE_ZIP_WARN_NO_WARNING', 0 );
59
  define( 'ARCHIVE_ZIP_WARN_FILE_EXIST', 1 );
60
 
61
  // ----- Methods parameters
62
  define( 'ARCHIVE_ZIP_PARAM_PATH', 'path' );
63
  define( 'ARCHIVE_ZIP_PARAM_ADD_PATH', 'add_path' );
64
  define( 'ARCHIVE_ZIP_PARAM_REMOVE_PATH', 'remove_path' );
65
  define( 'ARCHIVE_ZIP_PARAM_REMOVE_ALL_PATH', 'remove_all_path' );
66
  define( 'ARCHIVE_ZIP_PARAM_SET_CHMOD', 'set_chmod' );
67
  define( 'ARCHIVE_ZIP_PARAM_EXTRACT_AS_STRING', 'extract_as_string' );
68
  define( 'ARCHIVE_ZIP_PARAM_NO_COMPRESSION', 'no_compression' );
69
  define( 'ARCHIVE_ZIP_PARAM_BY_NAME', 'by_name' );
70
  define( 'ARCHIVE_ZIP_PARAM_BY_INDEX', 'by_index' );
71
  define( 'ARCHIVE_ZIP_PARAM_BY_EREG', 'by_ereg' );
72
  define( 'ARCHIVE_ZIP_PARAM_BY_PREG', 'by_preg' );
73
 
74
  define( 'ARCHIVE_ZIP_PARAM_PRE_EXTRACT', 'callback_pre_extract' );
75
  define( 'ARCHIVE_ZIP_PARAM_POST_EXTRACT', 'callback_post_extract' );
76
  define( 'ARCHIVE_ZIP_PARAM_PRE_ADD', 'callback_pre_add' );
77
  define( 'ARCHIVE_ZIP_PARAM_POST_ADD', 'callback_post_add' );
78
 
79
 
80
 
81
/**
82
* Class for manipulating zip archive files
83
*
84
* A class which provided common methods to manipulate ZIP formatted
85
* archive files.
86
* It provides creation, extraction, deletion and add features.
87
*
88
* @author   Vincent Blavet <vincent@blavet.net>
89
* @version  $Revision: 1.3 $
90
* @package  phing.lib
91
*/
92
class Archive_Zip
93
{
94
    /**
95
    * The filename of the zip archive.
96
    *
97
    * @var string Name of the Zip file
98
    */
99
    var $_zipname='';
100
 
101
    /**
102
    * File descriptor of the opened Zip file.
103
    *
104
    * @var int Internal zip file descriptor
105
    */
106
    var $_zip_fd=0;
107
 
108
    /**
109
    * @var int last error code
110
    */
111
    var $_error_code=1;
112
 
113
    /**
114
    * @var string Last error description
115
    */
116
    var $_error_string='';
117
 
118
    // {{{ constructor
119
    /**
120
    * Archive_Zip Class constructor. This flavour of the constructor only
121
    * declare a new Archive_Zip object, identifying it by the name of the
122
    * zip file.
123
    *
124
    * @param    string  $p_zipname  The name of the zip archive to create
125
    * @access public
126
    */
127
    function __construct($p_zipname)
128
    {
129
      if (!extension_loaded('zlib')) {
130
          throw new Exception("The extension 'zlib' couldn't be found.\n".
131
              "Please make sure your version of PHP was built ".
132
              "with 'zlib' support.");
133
      }
134
 
135
      // ----- Set the attributes
136
      $this->_zipname = $p_zipname;
137
      $this->_zip_fd = 0;
138
    }
139
    // }}}
140
 
141
    // {{{ create()
142
    /**
143
    * This method creates a Zip Archive with the filename set with
144
	* the constructor.
145
	* The files and directories indicated in $p_filelist
146
    * are added in the archive.
147
	* When a directory is in the list, the directory and its content is added
148
    * in the archive.
149
    * The methods takes a variable list of parameters in $p_params.
150
    * The supported parameters for this method are :
151
    *   'add_path' : Add a path to the archived files.
152
    *   'remove_path' : Remove the specified 'root' path of the archived files.
153
    *   'remove_all_path' : Remove all the path of the archived files.
154
    *   'no_compression' : The archived files will not be compressed.
155
    *
156
    * @access public
157
    * @param  mixed  $p_filelist  The list of the files or folders to add.
158
    *                             It can be a string with filenames separated
159
    *                             by a comma, or an array of filenames.
160
    * @param  mixed  $p_params  An array of variable parameters and values.
161
    * @return mixed An array of file description on success,
162
	*               an error code on error
163
    */
164
    function create($p_filelist, $p_params=0)
165
    {
166
        $this->_errorReset();
167
 
168
        // ----- Set default values
169
        if ($p_params === 0) {
170
    	    $p_params = array();
171
        }
172
        if ($this->_check_parameters($p_params,
173
	                                 array('no_compression' => false,
174
	                                       'add_path' => "",
175
	                                       'remove_path' => "",
176
	                                       'remove_all_path' => false)) != 1) {
177
		    return 0;
178
	    }
179
 
180
        // ----- Look if the $p_filelist is really an array
181
        $p_result_list = array();
182
        if (is_array($p_filelist)) {
183
            $v_result = $this->_create($p_filelist, $p_result_list, $p_params);
184
        }
185
 
186
        // ----- Look if the $p_filelist is a string
187
        else if (is_string($p_filelist)) {
188
            // ----- Create a list with the elements from the string
189
            $v_list = explode(ARCHIVE_ZIP_SEPARATOR, $p_filelist);
190
 
191
            $v_result = $this->_create($v_list, $p_result_list, $p_params);
192
        }
193
 
194
        // ----- Invalid variable
195
        else {
196
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
197
	                         'Invalid variable type p_filelist');
198
            $v_result = ARCHIVE_ZIP_ERR_INVALID_PARAMETER;
199
        }
200
 
201
        if ($v_result != 1) {
202
            return 0;
203
        }
204
 
205
        return $p_result_list;
206
    }
207
    // }}}
208
 
209
    // {{{ add()
210
    /**
211
    * This method add files or directory in an existing Zip Archive.
212
    * If the Zip Archive does not exist it is created.
213
	* The files and directories to add are indicated in $p_filelist.
214
	* When a directory is in the list, the directory and its content is added
215
    * in the archive.
216
    * The methods takes a variable list of parameters in $p_params.
217
    * The supported parameters for this method are :
218
    *   'add_path' : Add a path to the archived files.
219
    *   'remove_path' : Remove the specified 'root' path of the archived files.
220
    *   'remove_all_path' : Remove all the path of the archived files.
221
    *   'no_compression' : The archived files will not be compressed.
222
    *   'callback_pre_add' : A callback function that will be called before
223
    *                        each entry archiving.
224
    *   'callback_post_add' : A callback function that will be called after
225
    *                         each entry archiving.
226
    *
227
    * @access public
228
    * @param    mixed  $p_filelist  The list of the files or folders to add.
229
    *                               It can be a string with filenames separated
230
    *                               by a comma, or an array of filenames.
231
    * @param    mixed  $p_params  An array of variable parameters and values.
232
    * @return mixed An array of file description on success,
233
	*               0 on an unrecoverable failure, an error code is logged.
234
    */
235
    function add($p_filelist, $p_params=0)
236
    {
237
        $this->_errorReset();
238
 
239
        // ----- Set default values
240
        if ($p_params === 0) {
241
        	$p_params = array();
242
        }
243
        if ($this->_check_parameters($p_params,
244
	                                 array ('no_compression' => false,
245
	                                        'add_path' => '',
246
	                                        'remove_path' => '',
247
	                                        'remove_all_path' => false,
248
						    	     		'callback_pre_add' => '',
249
							    		    'callback_post_add' => '')) != 1) {
250
		    return 0;
251
	    }
252
 
253
        // ----- Look if the $p_filelist is really an array
254
        $p_result_list = array();
255
        if (is_array($p_filelist)) {
256
            // ----- Call the create fct
257
            $v_result = $this->_add($p_filelist, $p_result_list, $p_params);
258
        }
259
 
260
        // ----- Look if the $p_filelist is a string
261
        else if (is_string($p_filelist)) {
262
            // ----- Create a list with the elements from the string
263
            $v_list = explode(ARCHIVE_ZIP_SEPARATOR, $p_filelist);
264
 
265
            // ----- Call the create fct
266
            $v_result = $this->_add($v_list, $p_result_list, $p_params);
267
        }
268
 
269
        // ----- Invalid variable
270
        else {
271
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
272
	                         "add() : Invalid variable type p_filelist");
273
            $v_result = ARCHIVE_ZIP_ERR_INVALID_PARAMETER;
274
        }
275
 
276
        if ($v_result != 1) {
277
            return 0;
278
        }
279
 
280
        // ----- Return the result list
281
        return $p_result_list;
282
    }
283
    // }}}
284
 
285
    // {{{ listContent()
286
    /**
287
    * This method gives the names and properties of the files and directories
288
	* which are present in the zip archive.
289
    * The properties of each entries in the list are :
290
    *   filename : Name of the file.
291
	*              For create() or add() it's the filename given by the user.
292
	*              For an extract() it's the filename of the extracted file.
293
    *   stored_filename : Name of the file / directory stored in the archive.
294
    *   size : Size of the stored file.
295
    *   compressed_size : Size of the file's data compressed in the archive
296
    *                     (without the zip headers overhead)
297
    *   mtime : Last known modification date of the file (UNIX timestamp)
298
    *   comment : Comment associated with the file
299
    *   folder : true | false (indicates if the entry is a folder)
300
    *   index : index of the file in the archive (-1 when not available)
301
    *   status : status of the action on the entry (depending of the action) :
302
    *            Values are :
303
    *              ok : OK !
304
    *              filtered : the file/dir was not extracted (filtered by user)
305
    *              already_a_directory : the file can't be extracted because a
306
    *                                    directory with the same name already
307
	*                                    exists
308
    *              write_protected : the file can't be extracted because a file
309
    *                                with the same name already exists and is
310
    *                                write protected
311
    *              newer_exist : the file was not extracted because a newer
312
	*                            file already exists
313
    *              path_creation_fail : the file is not extracted because the
314
	*                                   folder does not exists and can't be
315
	*                                   created
316
    *              write_error : the file was not extracted because there was a
317
    *                            error while writing the file
318
    *              read_error : the file was not extracted because there was a
319
	*                           error while reading the file
320
    *              invalid_header : the file was not extracted because of an
321
	*                               archive format error (bad file header)
322
    * Note that each time a method can continue operating when there
323
    * is an error on a single file, the error is only logged in the file status.
324
    *
325
    * @access public
326
    * @return mixed An array of file description on success,
327
	*               0 on an unrecoverable failure, an error code is logged.
328
    */
329
    function listContent()
330
    {
331
        $this->_errorReset();
332
 
333
        // ----- Check archive
334
        if (!$this->_checkFormat()) {
335
            return(0);
336
        }
337
 
338
        $v_list = array();
339
        if ($this->_list($v_list) != 1) {
340
            unset($v_list);
341
            return(0);
342
        }
343
 
344
        return $v_list;
345
    }
346
    // }}}
347
 
348
    // {{{ extract()
349
    /**
350
    * This method extract the files and folders which are in the zip archive.
351
    * It can extract all the archive or a part of the archive by using filter
352
    * feature (extract by name, by index, by ereg, by preg). The extraction
353
    * can occur in the current path or an other path.
354
    * All the advanced features are activated by the use of variable
355
	* parameters.
356
	* The return value is an array of entry descriptions which gives
357
	* information on extracted files (See listContent()).
358
	* The method may return a success value (an array) even if some files
359
	* are not correctly extracted (see the file status in listContent()).
360
    * The supported variable parameters for this method are :
361
    *   'add_path' : Path where the files and directories are to be extracted
362
    *   'remove_path' : First part ('root' part) of the memorized path
363
    *                   (if similar) to remove while extracting.
364
    *   'remove_all_path' : Remove all the memorized path while extracting.
365
    *   'extract_as_string' :
366
    *   'set_chmod' : After the extraction of the file the indicated mode
367
    *                 will be set.
368
    *   'by_name' : It can be a string with file/dir names separated by ',',
369
    *               or an array of file/dir names to extract from the archive.
370
    *   'by_index' : A string with range of indexes separated by ',',
371
    *                (sample "1,3-5,12").
372
    *   'by_ereg' : A regular expression (ereg) that must match the extracted
373
    *               filename.
374
    *   'by_preg' : A regular expression (preg) that must match the extracted
375
    *               filename.
376
    *   'callback_pre_extract' : A callback function that will be called before
377
    *                            each entry extraction.
378
    *   'callback_post_extract' : A callback function that will be called after
379
    *                            each entry extraction.
380
    *
381
    * @access public
382
    * @param    mixed  $p_params  An array of variable parameters and values.
383
    * @return mixed An array of file description on success,
384
	*               0 on an unrecoverable failure, an error code is logged.
385
    */
386
    function extract($p_params=0)
387
    {
388
 
389
        $this->_errorReset();
390
 
391
        // ----- Check archive
392
        if (!$this->_checkFormat()) {
393
            return(0);
394
        }
395
 
396
        // ----- Set default values
397
        if ($p_params === 0) {
398
        	$p_params = array();
399
        }
400
        if ($this->_check_parameters($p_params,
401
	                                 array ('extract_as_string' => false,
402
	                                        'add_path' => '',
403
	                                        'remove_path' => '',
404
	                                        'remove_all_path' => false,
405
					    		     		'callback_pre_extract' => '',
406
						    			    'callback_post_extract' => '',
407
							    		    'set_chmod' => 0,
408
								    	    'by_name' => '',
409
									        'by_index' => '',
410
									        'by_ereg' => '',
411
									        'by_preg' => '') ) != 1) {
412
	    	return 0;
413
	    }
414
 
415
        // ----- Call the extracting fct
416
        $v_list = array();
417
        if ($this->_extractByRule($v_list, $p_params) != 1) {
418
            unset($v_list);
419
            return(0);
420
        }
421
 
422
        return $v_list;
423
    }
424
    // }}}
425
 
426
 
427
    // {{{ delete()
428
    /**
429
    * This methods delete archive entries in the zip archive.
430
    * Notice that at least one filtering rule (set by the variable parameter
431
    * list) must be set.
432
    * Also notice that if you delete a folder entry, only the folder entry
433
    * is deleted, not all the files bellonging to this folder.
434
    * The supported variable parameters for this method are :
435
    *   'by_name' : It can be a string with file/dir names separated by ',',
436
    *               or an array of file/dir names to delete from the archive.
437
    *   'by_index' : A string with range of indexes separated by ',',
438
    *                (sample "1,3-5,12").
439
    *   'by_ereg' : A regular expression (ereg) that must match the extracted
440
    *               filename.
441
    *   'by_preg' : A regular expression (preg) that must match the extracted
442
    *               filename.
443
    *
444
    * @access public
445
    * @param    mixed  $p_params  An array of variable parameters and values.
446
    * @return mixed An array of file description on success,
447
	*               0 on an unrecoverable failure, an error code is logged.
448
    */
449
    function delete($p_params)
450
    {
451
        $this->_errorReset();
452
 
453
        // ----- Check archive
454
        if (!$this->_checkFormat()) {
455
            return(0);
456
        }
457
 
458
        // ----- Set default values
459
        if ($this->_check_parameters($p_params,
460
	                                 array ('by_name' => '',
461
									        'by_index' => '',
462
									        'by_ereg' => '',
463
									        'by_preg' => '') ) != 1) {
464
	    	return 0;
465
    	}
466
 
467
        // ----- Check that at least one rule is set
468
        if (   ($p_params['by_name'] == '')
469
            && ($p_params['by_index'] == '')
470
            && ($p_params['by_ereg'] == '')
471
            && ($p_params['by_preg'] == '')) {
472
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
473
			                 'At least one filtering rule must'
474
							 .' be set as parameter');
475
            return 0;
476
        }
477
 
478
        // ----- Call the delete fct
479
        $v_list = array();
480
        if ($this->_deleteByRule($v_list, $p_params) != 1) {
481
            unset($v_list);
482
            return(0);
483
        }
484
 
485
        return $v_list;
486
    }
487
    // }}}
488
 
489
    // {{{ properties()
490
    /**
491
    * This method gives the global properties of the archive.
492
    *  The properties are :
493
    *    nb : Number of files in the archive
494
    *    comment : Comment associated with the archive file
495
    *    status : not_exist, ok
496
    *
497
    * @access public
498
    * @param    mixed  $p_params  {Description}
499
    * @return mixed An array with the global properties or 0 on error.
500
    */
501
    function properties()
502
    {
503
        $this->_errorReset();
504
 
505
        // ----- Check archive
506
        if (!$this->_checkFormat()) {
507
            return(0);
508
        }
509
 
510
        // ----- Default properties
511
        $v_prop = array();
512
        $v_prop['comment'] = '';
513
        $v_prop['nb'] = 0;
514
        $v_prop['status'] = 'not_exist';
515
 
516
        // ----- Look if file exists
517
        if (@is_file($this->_zipname)) {
518
            // ----- Open the zip file
519
            if (($this->_zip_fd = @fopen($this->_zipname, 'rb')) == 0) {
520
                $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
521
				                 'Unable to open archive \''.$this->_zipname
522
								 .'\' in binary read mode');
523
                return 0;
524
            }
525
 
526
            // ----- Read the central directory informations
527
            $v_central_dir = array();
528
            if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) {
529
                return 0;
530
            }
531
 
532
            $this->_closeFd();
533
 
534
            // ----- Set the user attributes
535
            $v_prop['comment'] = $v_central_dir['comment'];
536
            $v_prop['nb'] = $v_central_dir['entries'];
537
            $v_prop['status'] = 'ok';
538
        }
539
 
540
        return $v_prop;
541
    }
542
    // }}}
543
 
544
 
545
    // {{{ duplicate()
546
    /**
547
    * This method creates an archive by copying the content of an other one.
548
	* If the archive already exist, it is replaced by the new one without
549
	* any warning.
550
    *
551
    * @access public
552
    * @param  mixed  $p_archive  It can be a valid Archive_Zip object or
553
	*                            the filename of a valid zip archive.
554
    * @return integer 1 on success, 0 on failure.
555
    */
556
    function duplicate($p_archive)
557
    {
558
        $this->_errorReset();
559
 
560
        // ----- Look if the $p_archive is a Archive_Zip object
561
        if (   (is_object($p_archive))
562
		    && (strtolower(get_class($p_archive)) == 'archive_zip')) {
563
            $v_result = $this->_duplicate($p_archive->_zipname);
564
        }
565
 
566
        // ----- Look if the $p_archive is a string (so a filename)
567
        else if (is_string($p_archive)) {
568
            // ----- Check that $p_archive is a valid zip file
569
            // TBC : Should also check the archive format
570
            if (!is_file($p_archive)) {
571
                $this->_errorLog(ARCHIVE_ZIP_ERR_MISSING_FILE,
572
				                 "No file with filename '".$p_archive."'");
573
                $v_result = ARCHIVE_ZIP_ERR_MISSING_FILE;
574
            }
575
            else {
576
                $v_result = $this->_duplicate($p_archive);
577
            }
578
        }
579
 
580
        // ----- Invalid variable
581
        else {
582
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
583
			                 "Invalid variable type p_archive_to_add");
584
            $v_result = ARCHIVE_ZIP_ERR_INVALID_PARAMETER;
585
        }
586
 
587
        return $v_result;
588
    }
589
    // }}}
590
 
591
    // {{{ merge()
592
    /**
593
    *  This method merge a valid zip archive at the end of the
594
	*  archive identified by the Archive_Zip object.
595
    *  If the archive ($this) does not exist, the merge becomes a duplicate.
596
    *  If the archive to add does not exist, the merge is a success.
597
    *
598
    * @access public
599
    * @param mixed $p_archive_to_add  It can be a valid Archive_Zip object or
600
	*                                 the filename of a valid zip archive.
601
    * @return integer 1 on success, 0 on failure.
602
    */
603
    function merge($p_archive_to_add)
604
    {
605
        $v_result = 1;
606
        $this->_errorReset();
607
 
608
        // ----- Check archive
609
        if (!$this->_checkFormat()) {
610
            return(0);
611
        }
612
 
613
        // ----- Look if the $p_archive_to_add is a Archive_Zip object
614
        if (   (is_object($p_archive_to_add))
615
		    && (strtolower(get_class($p_archive_to_add)) == 'archive_zip')) {
616
            $v_result = $this->_merge($p_archive_to_add);
617
        }
618
 
619
        // ----- Look if the $p_archive_to_add is a string (so a filename)
620
        else if (is_string($p_archive_to_add)) {
621
            // ----- Create a temporary archive
622
            $v_object_archive = new Archive_Zip($p_archive_to_add);
623
 
624
            // ----- Merge the archive
625
            $v_result = $this->_merge($v_object_archive);
626
        }
627
 
628
        // ----- Invalid variable
629
        else {
630
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
631
			                 "Invalid variable type p_archive_to_add");
632
            $v_result = ARCHIVE_ZIP_ERR_INVALID_PARAMETER;
633
        }
634
 
635
        return $v_result;
636
    }
637
    // }}}
638
 
639
    // {{{ errorCode()
640
    /**
641
    * Method that gives the lastest error code.
642
    *
643
    * @access public
644
    * @return integer The error code value.
645
    */
646
    function errorCode()
647
    {
648
        return($this->_error_code);
649
    }
650
    // }}}
651
 
652
    // {{{ errorName()
653
    /**
654
    * This method gives the latest error code name.
655
    *
656
    * @access public
657
    * @param  boolean $p_with_code  If true, gives the name and the int value.
658
    * @return string The error name.
659
    */
660
    function errorName($p_with_code=false)
661
    {
662
        $v_const_list = get_defined_constants();
663
 
664
      	// ----- Extract error constants from all const.
665
        for (reset($v_const_list);
666
		     list($v_key, $v_value) = each($v_const_list);) {
667
     	    if (substr($v_key, 0, strlen('ARCHIVE_ZIP_ERR_'))
668
			    =='ARCHIVE_ZIP_ERR_') {
669
    		    $v_error_list[$v_key] = $v_value;
670
    	    }
671
        }
672
 
673
        // ----- Search the name form the code value
674
        $v_key=array_search($this->_error_code, $v_error_list, true);
675
  	    if ($v_key!=false) {
676
            $v_value = $v_key;
677
  	    }
678
  	    else {
679
            $v_value = 'NoName';
680
  	    }
681
 
682
        if ($p_with_code) {
683
            return($v_value.' ('.$this->_error_code.')');
684
        }
685
        else {
686
          return($v_value);
687
        }
688
    }
689
    // }}}
690
 
691
    // {{{ errorInfo()
692
    /**
693
    * This method returns the description associated with the latest error.
694
    *
695
    * @access public
696
    * @param  boolean $p_full If set to true gives the description with the
697
    *                         error code, the name and the description.
698
    *                         If set to false gives only the description
699
    *                         and the error code.
700
    * @return string The error description.
701
    */
702
    function errorInfo($p_full=false)
703
    {
704
        if ($p_full) {
705
            return($this->errorName(true)." : ".$this->_error_string);
706
        }
707
        else {
708
            return($this->_error_string." [code ".$this->_error_code."]");
709
        }
710
    }
711
    // }}}
712
 
713
 
714
// -----------------------------------------------------------------------------
715
// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS *****
716
// *****                                                        *****
717
// *****       THESES FUNCTIONS MUST NOT BE USED DIRECTLY       *****
718
// -----------------------------------------------------------------------------
719
 
720
  // ---------------------------------------------------------------------------
721
  // Function : _checkFormat()
722
  // Description :
723
  //   This method check that the archive exists and is a valid zip archive.
724
  //   Several level of check exists. (futur)
725
  // Parameters :
726
  //   $p_level : Level of check. Default 0.
727
  //              0 : Check the first bytes (magic codes) (default value))
728
  //              1 : 0 + Check the central directory (futur)
729
  //              2 : 1 + Check each file header (futur)
730
  // Return Values :
731
  //   true on success,
732
  //   false on error, the error code is set.
733
  // ---------------------------------------------------------------------------
734
  /**
735
  * Archive_Zip::_checkFormat()
736
  *
737
  * { Description }
738
  *
739
  * @param integer $p_level
740
  */
741
  function _checkFormat($p_level=0)
742
  {
743
    $v_result = true;
744
 
745
    // ----- Reset the error handler
746
    $this->_errorReset();
747
 
748
    // ----- Look if the file exits
749
    if (!is_file($this->_zipname)) {
750
      // ----- Error log
751
      $this->_errorLog(ARCHIVE_ZIP_ERR_MISSING_FILE,
752
	                   "Missing archive file '".$this->_zipname."'");
753
      return(false);
754
    }
755
 
756
    // ----- Check that the file is readeable
757
    if (!is_readable($this->_zipname)) {
758
      // ----- Error log
759
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
760
	                   "Unable to read archive '".$this->_zipname."'");
761
      return(false);
762
    }
763
 
764
    // ----- Check the magic code
765
    // TBC
766
 
767
    // ----- Check the central header
768
    // TBC
769
 
770
    // ----- Check each file header
771
    // TBC
772
 
773
    // ----- Return
774
    return $v_result;
775
  }
776
  // ---------------------------------------------------------------------------
777
 
778
  // ---------------------------------------------------------------------------
779
  // Function : _create()
780
  // Description :
781
  // Parameters :
782
  // Return Values :
783
  // ---------------------------------------------------------------------------
784
  /**
785
  * Archive_Zip::_create()
786
  *
787
  * { Description }
788
  *
789
  */
790
  function _create($p_list, &$p_result_list, &$p_params)
791
  {
792
    $v_result=1;
793
    $v_list_detail = array();
794
 
795
	$p_add_dir = $p_params['add_path'];
796
	$p_remove_dir = $p_params['remove_path'];
797
	$p_remove_all_dir = $p_params['remove_all_path'];
798
 
799
    // ----- Open the file in write mode
800
    if (($v_result = $this->_openFd('wb')) != 1)
801
    {
802
      // ----- Return
803
      return $v_result;
804
    }
805
 
806
    // ----- Add the list of files
807
    $v_result = $this->_addList($p_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params);
808
 
809
    // ----- Close
810
    $this->_closeFd();
811
 
812
    // ----- Return
813
    return $v_result;
814
  }
815
  // ---------------------------------------------------------------------------
816
 
817
  // ---------------------------------------------------------------------------
818
  // Function : _add()
819
  // Description :
820
  // Parameters :
821
  // Return Values :
822
  // ---------------------------------------------------------------------------
823
  /**
824
  * Archive_Zip::_add()
825
  *
826
  * { Description }
827
  *
828
  */
829
  function _add($p_list, &$p_result_list, &$p_params)
830
  {
831
    $v_result=1;
832
    $v_list_detail = array();
833
 
834
	$p_add_dir = $p_params['add_path'];
835
	$p_remove_dir = $p_params['remove_path'];
836
	$p_remove_all_dir = $p_params['remove_all_path'];
837
 
838
    // ----- Look if the archive exists or is empty and need to be created
839
    if ((!is_file($this->_zipname)) || (filesize($this->_zipname) == 0)) {
840
      $v_result = $this->_create($p_list, $p_result_list, $p_params);
841
      return $v_result;
842
    }
843
 
844
    // ----- Open the zip file
845
    if (($v_result=$this->_openFd('rb')) != 1) {
846
      return $v_result;
847
    }
848
 
849
    // ----- Read the central directory informations
850
    $v_central_dir = array();
851
    if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1)
852
    {
853
      $this->_closeFd();
854
      return $v_result;
855
    }
856
 
857
    // ----- Go to beginning of File
858
    @rewind($this->_zip_fd);
859
 
860
    // ----- Creates a temporay file
861
    $v_zip_temp_name = ARCHIVE_ZIP_TEMPORARY_DIR.uniqid('archive_zip-').'.tmp';
862
 
863
    // ----- Open the temporary file in write mode
864
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0)
865
    {
866
      $this->_closeFd();
867
 
868
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
869
	                   'Unable to open temporary file \''
870
					   .$v_zip_temp_name.'\' in binary write mode');
871
      return Archive_Zip::errorCode();
872
    }
873
 
874
    // ----- Copy the files from the archive to the temporary file
875
    // TBC : Here I should better append the file and go back to erase the
876
	// central dir
877
    $v_size = $v_central_dir['offset'];
878
    while ($v_size != 0)
879
    {
880
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
881
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
882
      $v_buffer = fread($this->_zip_fd, $v_read_size);
883
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
884
      $v_size -= $v_read_size;
885
    }
886
 
887
    // ----- Swap the file descriptor
888
    // Here is a trick : I swap the temporary fd with the zip fd, in order to
889
    // use the following methods on the temporary fil and not the real archive
890
    $v_swap = $this->_zip_fd;
891
    $this->_zip_fd = $v_zip_temp_fd;
892
    $v_zip_temp_fd = $v_swap;
893
 
894
    // ----- Add the files
895
    $v_header_list = array();
896
    if (($v_result = $this->_addFileList($p_list, $v_header_list,
897
	                                     $p_add_dir, $p_remove_dir,
898
										 $p_remove_all_dir, $p_params)) != 1)
899
    {
900
      fclose($v_zip_temp_fd);
901
      $this->_closeFd();
902
      @unlink($v_zip_temp_name);
903
 
904
      // ----- Return
905
      return $v_result;
906
    }
907
 
908
    // ----- Store the offset of the central dir
909
    $v_offset = @ftell($this->_zip_fd);
910
 
911
    // ----- Copy the block of file headers from the old archive
912
    $v_size = $v_central_dir['size'];
913
    while ($v_size != 0)
914
    {
915
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
916
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
917
      $v_buffer = @fread($v_zip_temp_fd, $v_read_size);
918
      @fwrite($this->_zip_fd, $v_buffer, $v_read_size);
919
      $v_size -= $v_read_size;
920
    }
921
 
922
    // ----- Create the Central Dir files header
923
    for ($i=0, $v_count=0; $i<sizeof($v_header_list); $i++)
924
    {
925
      // ----- Create the file header
926
      if ($v_header_list[$i]['status'] == 'ok') {
927
        if (($v_result=$this->_writeCentralFileHeader($v_header_list[$i]))!=1) {
928
          fclose($v_zip_temp_fd);
929
          $this->_closeFd();
930
          @unlink($v_zip_temp_name);
931
 
932
          // ----- Return
933
          return $v_result;
934
        }
935
        $v_count++;
936
      }
937
 
938
      // ----- Transform the header to a 'usable' info
939
      $this->_convertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
940
    }
941
 
942
    // ----- Zip file comment
943
    $v_comment = '';
944
 
945
    // ----- Calculate the size of the central header
946
    $v_size = @ftell($this->_zip_fd)-$v_offset;
947
 
948
    // ----- Create the central dir footer
949
    if (($v_result = $this->_writeCentralHeader($v_count
950
	                                              +$v_central_dir['entries'],
951
	                                            $v_size, $v_offset,
952
												$v_comment)) != 1) {
953
      // ----- Reset the file list
954
      unset($v_header_list);
955
 
956
      // ----- Return
957
      return $v_result;
958
    }
959
 
960
    // ----- Swap back the file descriptor
961
    $v_swap = $this->_zip_fd;
962
    $this->_zip_fd = $v_zip_temp_fd;
963
    $v_zip_temp_fd = $v_swap;
964
 
965
    // ----- Close
966
    $this->_closeFd();
967
 
968
    // ----- Close the temporary file
969
    @fclose($v_zip_temp_fd);
970
 
971
    // ----- Delete the zip file
972
    // TBC : I should test the result ...
973
    @unlink($this->_zipname);
974
 
975
    // ----- Rename the temporary file
976
    // TBC : I should test the result ...
977
    //@rename($v_zip_temp_name, $this->_zipname);
978
    $this->_tool_Rename($v_zip_temp_name, $this->_zipname);
979
 
980
    // ----- Return
981
    return $v_result;
982
  }
983
  // ---------------------------------------------------------------------------
984
 
985
  // ---------------------------------------------------------------------------
986
  // Function : _openFd()
987
  // Description :
988
  // Parameters :
989
  // ---------------------------------------------------------------------------
990
  /**
991
  * Archive_Zip::_openFd()
992
  *
993
  * { Description }
994
  *
995
  */
996
  function _openFd($p_mode)
997
  {
998
    $v_result=1;
999
 
1000
    // ----- Look if already open
1001
    if ($this->_zip_fd != 0)
1002
    {
1003
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
1004
	                   'Zip file \''.$this->_zipname.'\' already open');
1005
      return Archive_Zip::errorCode();
1006
    }
1007
 
1008
    // ----- Open the zip file
1009
    if (($this->_zip_fd = @fopen($this->_zipname, $p_mode)) == 0)
1010
    {
1011
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
1012
	                   'Unable to open archive \''.$this->_zipname
1013
					   .'\' in '.$p_mode.' mode');
1014
      return Archive_Zip::errorCode();
1015
    }
1016
 
1017
    // ----- Return
1018
    return $v_result;
1019
  }
1020
  // ---------------------------------------------------------------------------
1021
 
1022
  // ---------------------------------------------------------------------------
1023
  // Function : _closeFd()
1024
  // Description :
1025
  // Parameters :
1026
  // ---------------------------------------------------------------------------
1027
  /**
1028
  * Archive_Zip::_closeFd()
1029
  *
1030
  * { Description }
1031
  *
1032
  */
1033
  function _closeFd()
1034
  {
1035
    $v_result=1;
1036
 
1037
    if ($this->_zip_fd != 0)
1038
      @fclose($this->_zip_fd);
1039
    $this->_zip_fd = 0;
1040
 
1041
    // ----- Return
1042
    return $v_result;
1043
  }
1044
  // ---------------------------------------------------------------------------
1045
 
1046
  // ---------------------------------------------------------------------------
1047
  // Function : _addList()
1048
  // Description :
1049
  //   $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
1050
  //   different from the real path of the file. This is usefull if you want to have PclTar
1051
  //   running in any directory, and memorize relative path from an other directory.
1052
  // Parameters :
1053
  //   $p_list : An array containing the file or directory names to add in the tar
1054
  //   $p_result_list : list of added files with their properties (specially the status field)
1055
  //   $p_add_dir : Path to add in the filename path archived
1056
  //   $p_remove_dir : Path to remove in the filename path archived
1057
  // Return Values :
1058
  // ---------------------------------------------------------------------------
1059
  /**
1060
  * Archive_Zip::_addList()
1061
  *
1062
  * { Description }
1063
  *
1064
  */
1065
  function _addList($p_list, &$p_result_list,
1066
                    $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_params)
1067
  {
1068
    $v_result=1;
1069
 
1070
    // ----- Add the files
1071
    $v_header_list = array();
1072
    if (($v_result = $this->_addFileList($p_list, $v_header_list,
1073
	                                     $p_add_dir, $p_remove_dir,
1074
										 $p_remove_all_dir, $p_params)) != 1) {
1075
      return $v_result;
1076
    }
1077
 
1078
    // ----- Store the offset of the central dir
1079
    $v_offset = @ftell($this->_zip_fd);
1080
 
1081
    // ----- Create the Central Dir files header
1082
    for ($i=0,$v_count=0; $i<sizeof($v_header_list); $i++)
1083
    {
1084
      // ----- Create the file header
1085
      if ($v_header_list[$i]['status'] == 'ok') {
1086
        if (($v_result = $this->_writeCentralFileHeader($v_header_list[$i])) != 1) {
1087
          return $v_result;
1088
        }
1089
        $v_count++;
1090
      }
1091
 
1092
      // ----- Transform the header to a 'usable' info
1093
      $this->_convertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]);
1094
    }
1095
 
1096
    // ----- Zip file comment
1097
    $v_comment = '';
1098
 
1099
    // ----- Calculate the size of the central header
1100
    $v_size = @ftell($this->_zip_fd)-$v_offset;
1101
 
1102
    // ----- Create the central dir footer
1103
    if (($v_result = $this->_writeCentralHeader($v_count, $v_size, $v_offset,
1104
	                                            $v_comment)) != 1)
1105
    {
1106
      // ----- Reset the file list
1107
      unset($v_header_list);
1108
 
1109
      // ----- Return
1110
      return $v_result;
1111
    }
1112
 
1113
    // ----- Return
1114
    return $v_result;
1115
  }
1116
  // ---------------------------------------------------------------------------
1117
 
1118
  // ---------------------------------------------------------------------------
1119
  // Function : _addFileList()
1120
  // Description :
1121
  //   $p_add_dir and $p_remove_dir will give the ability to memorize a path which is
1122
  //   different from the real path of the file. This is usefull if you want to
1123
  //   run the lib in any directory, and memorize relative path from an other directory.
1124
  // Parameters :
1125
  //   $p_list : An array containing the file or directory names to add in the tar
1126
  //   $p_result_list : list of added files with their properties (specially the status field)
1127
  //   $p_add_dir : Path to add in the filename path archived
1128
  //   $p_remove_dir : Path to remove in the filename path archived
1129
  // Return Values :
1130
  // ---------------------------------------------------------------------------
1131
  /**
1132
  * Archive_Zip::_addFileList()
1133
  *
1134
  * { Description }
1135
  *
1136
  */
1137
  function _addFileList($p_list, &$p_result_list,
1138
                        $p_add_dir, $p_remove_dir, $p_remove_all_dir,
1139
						&$p_params)
1140
  {
1141
    $v_result=1;
1142
    $v_header = array();
1143
 
1144
    // ----- Recuperate the current number of elt in list
1145
    $v_nb = sizeof($p_result_list);
1146
 
1147
    // ----- Loop on the files
1148
    for ($j=0; ($j<count($p_list)) && ($v_result==1); $j++)
1149
    {
1150
      // ----- Recuperate the filename
1151
      $p_filename = $this->_tool_TranslateWinPath($p_list[$j], false);
1152
 
1153
      // ----- Skip empty file names
1154
      if ($p_filename == "")
1155
      {
1156
        continue;
1157
      }
1158
 
1159
      // ----- Check the filename
1160
      if (!file_exists($p_filename))
1161
      {
1162
        $this->_errorLog(ARCHIVE_ZIP_ERR_MISSING_FILE,
1163
		                 "File '$p_filename' does not exists");
1164
        return Archive_Zip::errorCode();
1165
      }
1166
 
1167
      // ----- Look if it is a file or a dir with no all pathnre move
1168
      if ((is_file($p_filename)) || ((is_dir($p_filename)) && !$p_remove_all_dir)) {
1169
        // ----- Add the file
1170
        if (($v_result = $this->_addFile($p_filename, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1)
1171
        {
1172
          // ----- Return status
1173
          return $v_result;
1174
        }
1175
 
1176
        // ----- Store the file infos
1177
        $p_result_list[$v_nb++] = $v_header;
1178
      }
1179
 
1180
      // ----- Look for directory
1181
      if (is_dir($p_filename))
1182
      {
1183
 
1184
        // ----- Look for path
1185
        if ($p_filename != ".")
1186
          $v_path = $p_filename."/";
1187
        else
1188
          $v_path = "";
1189
 
1190
        // ----- Read the directory for files and sub-directories
1191
        $p_hdir = opendir($p_filename);
1192
        $p_hitem = readdir($p_hdir); // '.' directory
1193
        $p_hitem = readdir($p_hdir); // '..' directory
1194
        while ($p_hitem = readdir($p_hdir))
1195
        {
1196
 
1197
          // ----- Look for a file
1198
          if (is_file($v_path.$p_hitem))
1199
          {
1200
 
1201
            // ----- Add the file
1202
            if (($v_result = $this->_addFile($v_path.$p_hitem, $v_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params)) != 1)
1203
            {
1204
              // ----- Return status
1205
              return $v_result;
1206
            }
1207
 
1208
            // ----- Store the file infos
1209
            $p_result_list[$v_nb++] = $v_header;
1210
          }
1211
 
1212
          // ----- Recursive call to _addFileList()
1213
          else
1214
          {
1215
 
1216
            // ----- Need an array as parameter
1217
            $p_temp_list[0] = $v_path.$p_hitem;
1218
            $v_result = $this->_addFileList($p_temp_list, $p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, $p_params);
1219
 
1220
            // ----- Update the number of elements of the list
1221
            $v_nb = sizeof($p_result_list);
1222
          }
1223
        }
1224
 
1225
        // ----- Free memory for the recursive loop
1226
        unset($p_temp_list);
1227
        unset($p_hdir);
1228
        unset($p_hitem);
1229
      }
1230
    }
1231
 
1232
    return $v_result;
1233
  }
1234
  // ---------------------------------------------------------------------------
1235
 
1236
  // ---------------------------------------------------------------------------
1237
  // Function : _addFile()
1238
  // Description :
1239
  // Parameters :
1240
  // Return Values :
1241
  // ---------------------------------------------------------------------------
1242
  /**
1243
  * Archive_Zip::_addFile()
1244
  *
1245
  * { Description }
1246
  *
1247
  */
1248
  function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_params)
1249
  {
1250
    $v_result=1;
1251
 
1252
    if ($p_filename == "")
1253
    {
1254
      // ----- Error log
1255
      $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)");
1256
 
1257
      // ----- Return
1258
      return Archive_Zip::errorCode();
1259
    }
1260
 
1261
    // ----- Calculate the stored filename
1262
    $v_stored_filename = $p_filename;
1263
 
1264
    // ----- Look for all path to remove
1265
    if ($p_remove_all_dir) {
1266
      $v_stored_filename = basename($p_filename);
1267
    }
1268
    // ----- Look for partial path remove
1269
    else if ($p_remove_dir != "")
1270
    {
1271
      $p_remove_dir = $this->_tool_TranslateWinPath($p_remove_dir, false);
1272
 
1273
      if (substr($p_remove_dir, -1) != '/')
1274
        $p_remove_dir .= "/";
1275
 
1276
      if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./"))
1277
      {
1278
        if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./"))
1279
          $p_remove_dir = "./".$p_remove_dir;
1280
        if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./"))
1281
          $p_remove_dir = substr($p_remove_dir, 2);
1282
      }
1283
 
1284
      $v_compare = $this->_tool_PathInclusion($p_remove_dir, $p_filename);
1285
      if ($v_compare > 0)
1286
//      if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
1287
      {
1288
 
1289
        if ($v_compare == 2) {
1290
          $v_stored_filename = "";
1291
        }
1292
        else {
1293
          $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
1294
        }
1295
      }
1296
    }
1297
    // ----- Look for path to add
1298
    if ($p_add_dir != "")
1299
    {
1300
      if (substr($p_add_dir, -1) == "/")
1301
        $v_stored_filename = $p_add_dir.$v_stored_filename;
1302
      else
1303
        $v_stored_filename = $p_add_dir."/".$v_stored_filename;
1304
    }
1305
 
1306
    // ----- Filename (reduce the path of stored name)
1307
    $v_stored_filename = $this->_tool_PathReduction($v_stored_filename);
1308
 
1309
 
1310
    /* filename length moved after call-back in release 1.3
1311
    // ----- Check the path length
1312
    if (strlen($v_stored_filename) > 0xFF)
1313
    {
1314
      // ----- Error log
1315
      $this->_errorLog(-5, "Stored file name is too long (max. 255) : '$v_stored_filename'");
1316
 
1317
      // ----- Return
1318
      return Archive_Zip::errorCode();
1319
    }
1320
    */
1321
 
1322
    // ----- Set the file properties
1323
    clearstatcache();
1324
    $p_header['version'] = 20;
1325
    $p_header['version_extracted'] = 10;
1326
    $p_header['flag'] = 0;
1327
    $p_header['compression'] = 0;
1328
    $p_header['mtime'] = filemtime($p_filename);
1329
    $p_header['crc'] = 0;
1330
    $p_header['compressed_size'] = 0;
1331
    $p_header['size'] = filesize($p_filename);
1332
    $p_header['filename_len'] = strlen($p_filename);
1333
    $p_header['extra_len'] = 0;
1334
    $p_header['comment_len'] = 0;
1335
    $p_header['disk'] = 0;
1336
    $p_header['internal'] = 0;
1337
    $p_header['external'] = (is_file($p_filename)?0xFE49FFE0:0x41FF0010);
1338
    $p_header['offset'] = 0;
1339
    $p_header['filename'] = $p_filename;
1340
    $p_header['stored_filename'] = $v_stored_filename;
1341
    $p_header['extra'] = '';
1342
    $p_header['comment'] = '';
1343
    $p_header['status'] = 'ok';
1344
    $p_header['index'] = -1;
1345
 
1346
    // ----- Look for pre-add callback
1347
    if (   (isset($p_params[ARCHIVE_ZIP_PARAM_PRE_ADD]))
1348
	    && ($p_params[ARCHIVE_ZIP_PARAM_PRE_ADD] != '')) {
1349
 
1350
      // ----- Generate a local information
1351
      $v_local_header = array();
1352
      $this->_convertHeader2FileInfo($p_header, $v_local_header);
1353
 
1354
      // ----- Call the callback
1355
      // Here I do not use call_user_func() because I need to send a reference to the
1356
      // header.
1357
      eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_PRE_ADD].'(ARCHIVE_ZIP_PARAM_PRE_ADD, $v_local_header);');
1358
      if ($v_result == 0) {
1359
        // ----- Change the file status
1360
        $p_header['status'] = "skipped";
1361
        $v_result = 1;
1362
      }
1363
 
1364
      // ----- Update the informations
1365
      // Only some fields can be modified
1366
      if ($p_header['stored_filename'] != $v_local_header['stored_filename']) {
1367
        $p_header['stored_filename'] = $this->_tool_PathReduction($v_local_header['stored_filename']);
1368
      }
1369
    }
1370
 
1371
    // ----- Look for empty stored filename
1372
    if ($p_header['stored_filename'] == "") {
1373
      $p_header['status'] = "filtered";
1374
    }
1375
 
1376
    // ----- Check the path length
1377
    if (strlen($p_header['stored_filename']) > 0xFF) {
1378
      $p_header['status'] = 'filename_too_long';
1379
    }
1380
 
1381
    // ----- Look if no error, or file not skipped
1382
    if ($p_header['status'] == 'ok') {
1383
 
1384
      // ----- Look for a file
1385
      if (is_file($p_filename))
1386
      {
1387
        // ----- Open the source file
1388
        if (($v_file = @fopen($p_filename, "rb")) == 0) {
1389
          $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode");
1390
          return Archive_Zip::errorCode();
1391
        }
1392
 
1393
        if ($p_params['no_compression']) {
1394
          // ----- Read the file content
1395
          $v_content_compressed = @fread($v_file, $p_header['size']);
1396
 
1397
          // ----- Calculate the CRC
1398
          $p_header['crc'] = crc32($v_content_compressed);
1399
        }
1400
        else {
1401
          // ----- Read the file content
1402
          $v_content = @fread($v_file, $p_header['size']);
1403
 
1404
          // ----- Calculate the CRC
1405
          $p_header['crc'] = crc32($v_content);
1406
 
1407
          // ----- Compress the file
1408
          $v_content_compressed = gzdeflate($v_content);
1409
        }
1410
 
1411
        // ----- Set header parameters
1412
        $p_header['compressed_size'] = strlen($v_content_compressed);
1413
        $p_header['compression'] = 8;
1414
 
1415
        // ----- Call the header generation
1416
        if (($v_result = $this->_writeFileHeader($p_header)) != 1) {
1417
          @fclose($v_file);
1418
          return $v_result;
1419
        }
1420
 
1421
        // ----- Write the compressed content
1422
        $v_binary_data = pack('a'.$p_header['compressed_size'], $v_content_compressed);
1423
        @fwrite($this->_zip_fd, $v_binary_data, $p_header['compressed_size']);
1424
 
1425
        // ----- Close the file
1426
        @fclose($v_file);
1427
      }
1428
 
1429
      // ----- Look for a directory
1430
      else
1431
      {
1432
        // ----- Set the file properties
1433
        $p_header['filename'] .= '/';
1434
        $p_header['filename_len']++;
1435
        $p_header['size'] = 0;
1436
        $p_header['external'] = 0x41FF0010;   // Value for a folder : to be checked
1437
 
1438
        // ----- Call the header generation
1439
        if (($v_result = $this->_writeFileHeader($p_header)) != 1)
1440
        {
1441
          return $v_result;
1442
        }
1443
      }
1444
    }
1445
 
1446
    // ----- Look for pre-add callback
1447
    if (   (isset($p_params[ARCHIVE_ZIP_PARAM_POST_ADD]))
1448
	    && ($p_params[ARCHIVE_ZIP_PARAM_POST_ADD] != '')) {
1449
 
1450
      // ----- Generate a local information
1451
      $v_local_header = array();
1452
      $this->_convertHeader2FileInfo($p_header, $v_local_header);
1453
 
1454
      // ----- Call the callback
1455
      // Here I do not use call_user_func() because I need to send a reference to the
1456
      // header.
1457
      eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_POST_ADD].'(ARCHIVE_ZIP_PARAM_POST_ADD, $v_local_header);');
1458
      if ($v_result == 0) {
1459
        // ----- Ignored
1460
        $v_result = 1;
1461
      }
1462
 
1463
      // ----- Update the informations
1464
      // Nothing can be modified
1465
    }
1466
 
1467
    // ----- Return
1468
    return $v_result;
1469
  }
1470
  // ---------------------------------------------------------------------------
1471
 
1472
  // ---------------------------------------------------------------------------
1473
  // Function : _writeFileHeader()
1474
  // Description :
1475
  // Parameters :
1476
  // Return Values :
1477
  // ---------------------------------------------------------------------------
1478
  /**
1479
  * Archive_Zip::_writeFileHeader()
1480
  *
1481
  * { Description }
1482
  *
1483
  */
1484
  function _writeFileHeader(&$p_header)
1485
  {
1486
    $v_result=1;
1487
 
1488
    // TBC
1489
    //for(reset($p_header); $key = key($p_header); next($p_header)) {
1490
    //}
1491
 
1492
    // ----- Store the offset position of the file
1493
    $p_header['offset'] = ftell($this->_zip_fd);
1494
 
1495
    // ----- Transform UNIX mtime to DOS format mdate/mtime
1496
    $v_date = getdate($p_header['mtime']);
1497
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
1498
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
1499
 
1500
    // ----- Packed data
1501
    $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version'], $p_header['flag'],
1502
                          $p_header['compression'], $v_mtime, $v_mdate,
1503
                          $p_header['crc'], $p_header['compressed_size'], $p_header['size'],
1504
                          strlen($p_header['stored_filename']), $p_header['extra_len']);
1505
 
1506
    // ----- Write the first 148 bytes of the header in the archive
1507
    fputs($this->_zip_fd, $v_binary_data, 30);
1508
 
1509
    // ----- Write the variable fields
1510
    if (strlen($p_header['stored_filename']) != 0)
1511
    {
1512
      fputs($this->_zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
1513
    }
1514
    if ($p_header['extra_len'] != 0)
1515
    {
1516
      fputs($this->_zip_fd, $p_header['extra'], $p_header['extra_len']);
1517
    }
1518
 
1519
    // ----- Return
1520
    return $v_result;
1521
  }
1522
  // ---------------------------------------------------------------------------
1523
 
1524
  // ---------------------------------------------------------------------------
1525
  // Function : _writeCentralFileHeader()
1526
  // Description :
1527
  // Parameters :
1528
  // Return Values :
1529
  // ---------------------------------------------------------------------------
1530
  /**
1531
  * Archive_Zip::_writeCentralFileHeader()
1532
  *
1533
  * { Description }
1534
  *
1535
  */
1536
  function _writeCentralFileHeader(&$p_header)
1537
  {
1538
    $v_result=1;
1539
 
1540
    // TBC
1541
    //for(reset($p_header); $key = key($p_header); next($p_header)) {
1542
    //}
1543
 
1544
    // ----- Transform UNIX mtime to DOS format mdate/mtime
1545
    $v_date = getdate($p_header['mtime']);
1546
    $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2;
1547
    $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday'];
1548
 
1549
    // ----- Packed data
1550
    $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'],
1551
                          $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'],
1552
                          $p_header['compressed_size'], $p_header['size'],
1553
                          strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'],
1554
                          $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']);
1555
 
1556
    // ----- Write the 42 bytes of the header in the zip file
1557
    fputs($this->_zip_fd, $v_binary_data, 46);
1558
 
1559
    // ----- Write the variable fields
1560
    if (strlen($p_header['stored_filename']) != 0)
1561
    {
1562
      fputs($this->_zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename']));
1563
    }
1564
    if ($p_header['extra_len'] != 0)
1565
    {
1566
      fputs($this->_zip_fd, $p_header['extra'], $p_header['extra_len']);
1567
    }
1568
    if ($p_header['comment_len'] != 0)
1569
    {
1570
      fputs($this->_zip_fd, $p_header['comment'], $p_header['comment_len']);
1571
    }
1572
 
1573
    // ----- Return
1574
    return $v_result;
1575
  }
1576
  // ---------------------------------------------------------------------------
1577
 
1578
  // ---------------------------------------------------------------------------
1579
  // Function : _writeCentralHeader()
1580
  // Description :
1581
  // Parameters :
1582
  // Return Values :
1583
  // ---------------------------------------------------------------------------
1584
  /**
1585
  * Archive_Zip::_writeCentralHeader()
1586
  *
1587
  * { Description }
1588
  *
1589
  */
1590
  function _writeCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment)
1591
  {
1592
    $v_result=1;
1593
 
1594
    // ----- Packed data
1595
    $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment));
1596
 
1597
    // ----- Write the 22 bytes of the header in the zip file
1598
    fputs($this->_zip_fd, $v_binary_data, 22);
1599
 
1600
    // ----- Write the variable fields
1601
    if (strlen($p_comment) != 0)
1602
    {
1603
      fputs($this->_zip_fd, $p_comment, strlen($p_comment));
1604
    }
1605
 
1606
    // ----- Return
1607
    return $v_result;
1608
  }
1609
  // ---------------------------------------------------------------------------
1610
 
1611
  // ---------------------------------------------------------------------------
1612
  // Function : _list()
1613
  // Description :
1614
  // Parameters :
1615
  // Return Values :
1616
  // ---------------------------------------------------------------------------
1617
  /**
1618
  * Archive_Zip::_list()
1619
  *
1620
  * { Description }
1621
  *
1622
  */
1623
  function _list(&$p_list)
1624
  {
1625
    $v_result=1;
1626
 
1627
    // ----- Open the zip file
1628
    if (($this->_zip_fd = @fopen($this->_zipname, 'rb')) == 0)
1629
    {
1630
      // ----- Error log
1631
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->_zipname.'\' in binary read mode');
1632
 
1633
      // ----- Return
1634
      return Archive_Zip::errorCode();
1635
    }
1636
 
1637
    // ----- Read the central directory informations
1638
    $v_central_dir = array();
1639
    if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1)
1640
    {
1641
      return $v_result;
1642
    }
1643
 
1644
    // ----- Go to beginning of Central Dir
1645
    @rewind($this->_zip_fd);
1646
    if (@fseek($this->_zip_fd, $v_central_dir['offset']))
1647
    {
1648
      // ----- Error log
1649
      $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
1650
 
1651
      // ----- Return
1652
      return Archive_Zip::errorCode();
1653
    }
1654
 
1655
    // ----- Read each entry
1656
    for ($i=0; $i<$v_central_dir['entries']; $i++)
1657
    {
1658
      // ----- Read the file header
1659
      if (($v_result = $this->_readCentralFileHeader($v_header)) != 1)
1660
      {
1661
        return $v_result;
1662
      }
1663
      $v_header['index'] = $i;
1664
 
1665
      // ----- Get the only interesting attributes
1666
      $this->_convertHeader2FileInfo($v_header, $p_list[$i]);
1667
      unset($v_header);
1668
    }
1669
 
1670
    // ----- Close the zip file
1671
    $this->_closeFd();
1672
 
1673
    // ----- Return
1674
    return $v_result;
1675
  }
1676
  // ---------------------------------------------------------------------------
1677
 
1678
  // ---------------------------------------------------------------------------
1679
  // Function : _convertHeader2FileInfo()
1680
  // Description :
1681
  //   This function takes the file informations from the central directory
1682
  //   entries and extract the interesting parameters that will be given back.
1683
  //   The resulting file infos are set in the array $p_info
1684
  //     $p_info['filename'] : Filename with full path. Given by user (add),
1685
  //                           extracted in the filesystem (extract).
1686
  //     $p_info['stored_filename'] : Stored filename in the archive.
1687
  //     $p_info['size'] = Size of the file.
1688
  //     $p_info['compressed_size'] = Compressed size of the file.
1689
  //     $p_info['mtime'] = Last modification date of the file.
1690
  //     $p_info['comment'] = Comment associated with the file.
1691
  //     $p_info['folder'] = true/false : indicates if the entry is a folder or not.
1692
  //     $p_info['status'] = status of the action on the file.
1693
  // Parameters :
1694
  // Return Values :
1695
  // ---------------------------------------------------------------------------
1696
  /**
1697
  * Archive_Zip::_convertHeader2FileInfo()
1698
  *
1699
  * { Description }
1700
  *
1701
  */
1702
  function _convertHeader2FileInfo($p_header, &$p_info)
1703
  {
1704
    $v_result=1;
1705
 
1706
    // ----- Get the interesting attributes
1707
    $p_info['filename'] = $p_header['filename'];
1708
    $p_info['stored_filename'] = $p_header['stored_filename'];
1709
    $p_info['size'] = $p_header['size'];
1710
    $p_info['compressed_size'] = $p_header['compressed_size'];
1711
    $p_info['mtime'] = $p_header['mtime'];
1712
    $p_info['comment'] = $p_header['comment'];
1713
    $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
1714
    $p_info['index'] = $p_header['index'];
1715
    $p_info['status'] = $p_header['status'];
1716
 
1717
    // ----- Return
1718
    return $v_result;
1719
  }
1720
  // ---------------------------------------------------------------------------
1721
 
1722
  // ---------------------------------------------------------------------------
1723
  // Function : _extractByRule()
1724
  // Description :
1725
  //   Extract a file or directory depending of rules (by index, by name, ...)
1726
  // Parameters :
1727
  //   $p_file_list : An array where will be placed the properties of each
1728
  //                  extracted file
1729
  //   $p_path : Path to add while writing the extracted files
1730
  //   $p_remove_path : Path to remove (from the file memorized path) while writing the
1731
  //                    extracted files. If the path does not match the file path,
1732
  //                    the file is extracted with its memorized path.
1733
  //                    $p_remove_path does not apply to 'list' mode.
1734
  //                    $p_path and $p_remove_path are commulative.
1735
  // Return Values :
1736
  //   1 on success,0 or less on error (see error code list)
1737
  // ---------------------------------------------------------------------------
1738
  /**
1739
  * Archive_Zip::_extractByRule()
1740
  *
1741
  * { Description }
1742
  *
1743
  */
1744
  function _extractByRule(&$p_file_list, &$p_params)
1745
  {
1746
    $v_result=1;
1747
 
1748
	$p_path = $p_params['add_path'];
1749
	$p_remove_path = $p_params['remove_path'];
1750
	$p_remove_all_path = $p_params['remove_all_path'];
1751
 
1752
    // ----- Check the path
1753
    if (($p_path == "")
1754
	    || ((substr($p_path, 0, 1) != "/")
1755
	    && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/")))
1756
      $p_path = "./".$p_path;
1757
 
1758
    // ----- Reduce the path last (and duplicated) '/'
1759
    if (($p_path != "./") && ($p_path != "/")) {
1760
      // ----- Look for the path end '/'
1761
      while (substr($p_path, -1) == "/") {
1762
        $p_path = substr($p_path, 0, strlen($p_path)-1);
1763
      }
1764
    }
1765
 
1766
    // ----- Look for path to remove format (should end by /)
1767
    if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) {
1768
      $p_remove_path .= '/';
1769
    }
1770
    $p_remove_path_size = strlen($p_remove_path);
1771
 
1772
    // ----- Open the zip file
1773
    if (($v_result = $this->_openFd('rb')) != 1)
1774
    {
1775
      return $v_result;
1776
    }
1777
 
1778
    // ----- Read the central directory informations
1779
    $v_central_dir = array();
1780
    if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1)
1781
    {
1782
      // ----- Close the zip file
1783
      $this->_closeFd();
1784
 
1785
      return $v_result;
1786
    }
1787
 
1788
    // ----- Start at beginning of Central Dir
1789
    $v_pos_entry = $v_central_dir['offset'];
1790
 
1791
    // ----- Read each entry
1792
    $j_start = 0;
1793
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) {
1794
      // ----- Read next Central dir entry
1795
      @rewind($this->_zip_fd);
1796
      if (@fseek($this->_zip_fd, $v_pos_entry)) {
1797
        $this->_closeFd();
1798
 
1799
        $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP,
1800
		                 'Invalid archive size');
1801
 
1802
        return Archive_Zip::errorCode();
1803
      }
1804
 
1805
      // ----- Read the file header
1806
      $v_header = array();
1807
      if (($v_result = $this->_readCentralFileHeader($v_header)) != 1) {
1808
        $this->_closeFd();
1809
 
1810
        return $v_result;
1811
      }
1812
 
1813
      // ----- Store the index
1814
      $v_header['index'] = $i;
1815
 
1816
      // ----- Store the file position
1817
      $v_pos_entry = ftell($this->_zip_fd);
1818
 
1819
      // ----- Look for the specific extract rules
1820
      $v_extract = false;
1821
 
1822
      // ----- Look for extract by name rule
1823
      if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_NAME]))
1824
          && ($p_params[ARCHIVE_ZIP_PARAM_BY_NAME] != 0)) {
1825
 
1826
          // ----- Look if the filename is in the list
1827
          for ($j=0;
1828
		          ($j<sizeof($p_params[ARCHIVE_ZIP_PARAM_BY_NAME]))
1829
			   && (!$v_extract);
1830
			   $j++) {
1831
 
1832
              // ----- Look for a directory
1833
              if (substr($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j], -1) == "/") {
1834
 
1835
                  // ----- Look if the directory is in the filename path
1836
                  if (   (strlen($v_header['stored_filename']) > strlen($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j]))
1837
                      && (substr($v_header['stored_filename'], 0, strlen($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j])) == $p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j])) {
1838
                      $v_extract = true;
1839
                  }
1840
              }
1841
              // ----- Look for a filename
1842
              elseif ($v_header['stored_filename'] == $p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j]) {
1843
                  $v_extract = true;
1844
              }
1845
          }
1846
      }
1847
 
1848
      // ----- Look for extract by ereg rule
1849
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_EREG]))
1850
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_EREG] != "")) {
1851
 
1852
          if (ereg($p_params[ARCHIVE_ZIP_PARAM_BY_EREG], $v_header['stored_filename'])) {
1853
              $v_extract = true;
1854
          }
1855
      }
1856
 
1857
      // ----- Look for extract by preg rule
1858
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_PREG]))
1859
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_PREG] != "")) {
1860
 
1861
          if (preg_match($p_params[ARCHIVE_ZIP_PARAM_BY_PREG], $v_header['stored_filename'])) {
1862
              $v_extract = true;
1863
          }
1864
      }
1865
 
1866
      // ----- Look for extract by index rule
1867
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX]))
1868
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX] != 0)) {
1869
 
1870
          // ----- Look if the index is in the list
1871
          for ($j=$j_start; ($j<sizeof($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX])) && (!$v_extract); $j++) {
1872
 
1873
              if (($i>=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['start']) && ($i<=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['end'])) {
1874
                  $v_extract = true;
1875
              }
1876
              if ($i>=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['end']) {
1877
                  $j_start = $j+1;
1878
              }
1879
 
1880
              if ($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['start']>$i) {
1881
                  break;
1882
              }
1883
          }
1884
      }
1885
 
1886
      // ----- Look for no rule, which means extract all the archive
1887
      else {
1888
          $v_extract = true;
1889
      }
1890
 
1891
 
1892
      // ----- Look for real extraction
1893
      if ($v_extract)
1894
      {
1895
 
1896
        // ----- Go to the file position
1897
        @rewind($this->_zip_fd);
1898
        if (@fseek($this->_zip_fd, $v_header['offset']))
1899
        {
1900
          // ----- Close the zip file
1901
          $this->_closeFd();
1902
 
1903
          // ----- Error log
1904
          $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
1905
 
1906
          // ----- Return
1907
          return Archive_Zip::errorCode();
1908
        }
1909
 
1910
        // ----- Look for extraction as string
1911
        if ($p_params[ARCHIVE_ZIP_PARAM_EXTRACT_AS_STRING]) {
1912
 
1913
          // ----- Extracting the file
1914
          if (($v_result = $this->_extractFileAsString($v_header, $v_string)) != 1)
1915
          {
1916
            // ----- Close the zip file
1917
            $this->_closeFd();
1918
 
1919
            return $v_result;
1920
          }
1921
 
1922
          // ----- Get the only interesting attributes
1923
          if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1)
1924
          {
1925
            // ----- Close the zip file
1926
            $this->_closeFd();
1927
 
1928
            return $v_result;
1929
          }
1930
 
1931
          // ----- Set the file content
1932
          $p_file_list[$v_nb_extracted]['content'] = $v_string;
1933
 
1934
          // ----- Next extracted file
1935
          $v_nb_extracted++;
1936
        }
1937
        else {
1938
          // ----- Extracting the file
1939
          if (($v_result = $this->_extractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_params)) != 1)
1940
          {
1941
            // ----- Close the zip file
1942
            $this->_closeFd();
1943
 
1944
            return $v_result;
1945
          }
1946
 
1947
          // ----- Get the only interesting attributes
1948
          if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
1949
          {
1950
            // ----- Close the zip file
1951
            $this->_closeFd();
1952
 
1953
            return $v_result;
1954
          }
1955
        }
1956
      }
1957
    }
1958
 
1959
    // ----- Close the zip file
1960
    $this->_closeFd();
1961
 
1962
    // ----- Return
1963
    return $v_result;
1964
  }
1965
  // ---------------------------------------------------------------------------
1966
 
1967
  // ---------------------------------------------------------------------------
1968
  // Function : _extractFile()
1969
  // Description :
1970
  // Parameters :
1971
  // Return Values :
1972
  // ---------------------------------------------------------------------------
1973
  /**
1974
  * Archive_Zip::_extractFile()
1975
  *
1976
  * { Description }
1977
  *
1978
  */
1979
  function _extractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_params)
1980
  {
1981
    $v_result=1;
1982
 
1983
    // ----- Read the file header
1984
    if (($v_result = $this->_readFileHeader($v_header)) != 1)
1985
    {
1986
      // ----- Return
1987
      return $v_result;
1988
    }
1989
 
1990
 
1991
    // ----- Check that the file header is coherent with $p_entry info
1992
    // TBC
1993
 
1994
    // ----- Look for all path to remove
1995
    if ($p_remove_all_path == true) {
1996
        // ----- Get the basename of the path
1997
        $p_entry['filename'] = basename($p_entry['filename']);
1998
    }
1999
 
2000
    // ----- Look for path to remove
2001
    else if ($p_remove_path != "")
2002
    {
2003
      //if (strcmp($p_remove_path, $p_entry['filename'])==0)
2004
      if ($this->_tool_PathInclusion($p_remove_path, $p_entry['filename']) == 2)
2005
      {
2006
 
2007
        // ----- Change the file status
2008
        $p_entry['status'] = "filtered";
2009
 
2010
        // ----- Return
2011
        return $v_result;
2012
      }
2013
 
2014
      $p_remove_path_size = strlen($p_remove_path);
2015
      if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
2016
      {
2017
 
2018
        // ----- Remove the path
2019
        $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);
2020
 
2021
      }
2022
    }
2023
 
2024
    // ----- Add the path
2025
    if ($p_path != '')
2026
    {
2027
      $p_entry['filename'] = $p_path."/".$p_entry['filename'];
2028
    }
2029
 
2030
    // ----- Look for pre-extract callback
2031
    if (   (isset($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT]))
2032
	    && ($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT] != '')) {
2033
 
2034
      // ----- Generate a local information
2035
      $v_local_header = array();
2036
      $this->_convertHeader2FileInfo($p_entry, $v_local_header);
2037
 
2038
      // ----- Call the callback
2039
      // Here I do not use call_user_func() because I need to send a reference to the
2040
      // header.
2041
      eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT].'(ARCHIVE_ZIP_PARAM_PRE_EXTRACT, $v_local_header);');
2042
      if ($v_result == 0) {
2043
        // ----- Change the file status
2044
        $p_entry['status'] = "skipped";
2045
        $v_result = 1;
2046
      }
2047
 
2048
      // ----- Update the informations
2049
      // Only some fields can be modified
2050
      $p_entry['filename'] = $v_local_header['filename'];
2051
    }
2052
 
2053
    // ----- Trace
2054
 
2055
    // ----- Look if extraction should be done
2056
    if ($p_entry['status'] == 'ok') {
2057
 
2058
    // ----- Look for specific actions while the file exist
2059
    if (file_exists($p_entry['filename']))
2060
    {
2061
 
2062
      // ----- Look if file is a directory
2063
      if (is_dir($p_entry['filename']))
2064
      {
2065
 
2066
        // ----- Change the file status
2067
        $p_entry['status'] = "already_a_directory";
2068
 
2069
        // ----- Return
2070
        //return $v_result;
2071
      }
2072
      // ----- Look if file is write protected
2073
      else if (!is_writeable($p_entry['filename']))
2074
      {
2075
 
2076
        // ----- Change the file status
2077
        $p_entry['status'] = "write_protected";
2078
 
2079
        // ----- Return
2080
        //return $v_result;
2081
      }
2082
 
2083
      // ----- Look if the extracted file is older
2084
      else if (filemtime($p_entry['filename']) > $p_entry['mtime'])
2085
      {
2086
 
2087
        // ----- Change the file status
2088
        $p_entry['status'] = "newer_exist";
2089
 
2090
        // ----- Return
2091
        //return $v_result;
2092
      }
2093
    }
2094
 
2095
    // ----- Check the directory availability and create it if necessary
2096
    else {
2097
      if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
2098
        $v_dir_to_check = $p_entry['filename'];
2099
      else if (!strstr($p_entry['filename'], "/"))
2100
        $v_dir_to_check = "";
2101
      else
2102
        $v_dir_to_check = dirname($p_entry['filename']);
2103
 
2104
      if (($v_result = $this->_dirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {
2105
 
2106
        // ----- Change the file status
2107
        $p_entry['status'] = "path_creation_fail";
2108
 
2109
        // ----- Return
2110
        //return $v_result;
2111
        $v_result = 1;
2112
      }
2113
    }
2114
    }
2115
 
2116
    // ----- Look if extraction should be done
2117
    if ($p_entry['status'] == 'ok') {
2118
 
2119
      // ----- Do the extraction (if not a folder)
2120
      if (!(($p_entry['external']&0x00000010)==0x00000010))
2121
      {
2122
 
2123
        // ----- Look for not compressed file
2124
        if ($p_entry['compressed_size'] == $p_entry['size'])
2125
        {
2126
 
2127
          // ----- Opening destination file
2128
          if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0)
2129
          {
2130
 
2131
            // ----- Change the file status
2132
            $p_entry['status'] = "write_error";
2133
 
2134
            // ----- Return
2135
            return $v_result;
2136
          }
2137
 
2138
 
2139
          // ----- Read the file by ARCHIVE_ZIP_READ_BLOCK_SIZE octets blocks
2140
          $v_size = $p_entry['compressed_size'];
2141
          while ($v_size != 0)
2142
          {
2143
            $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
2144
            $v_buffer = fread($this->_zip_fd, $v_read_size);
2145
            $v_binary_data = pack('a'.$v_read_size, $v_buffer);
2146
            @fwrite($v_dest_file, $v_binary_data, $v_read_size);
2147
            $v_size -= $v_read_size;
2148
          }
2149
 
2150
          // ----- Closing the destination file
2151
          fclose($v_dest_file);
2152
 
2153
          // ----- Change the file mtime
2154
          touch($p_entry['filename'], $p_entry['mtime']);
2155
        }
2156
        else
2157
        {
2158
          // ----- Trace
2159
 
2160
          // ----- Opening destination file
2161
          if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
2162
 
2163
            // ----- Change the file status
2164
            $p_entry['status'] = "write_error";
2165
 
2166
            return $v_result;
2167
          }
2168
 
2169
 
2170
          // ----- Read the compressed file in a buffer (one shot)
2171
          $v_buffer = @fread($this->_zip_fd, $p_entry['compressed_size']);
2172
 
2173
          // ----- Decompress the file
2174
          $v_file_content = gzinflate($v_buffer);
2175
          unset($v_buffer);
2176
 
2177
          // ----- Write the uncompressed data
2178
          @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
2179
          unset($v_file_content);
2180
 
2181
          // ----- Closing the destination file
2182
          @fclose($v_dest_file);
2183
 
2184
          // ----- Change the file mtime
2185
          touch($p_entry['filename'], $p_entry['mtime']);
2186
        }
2187
 
2188
        // ----- Look for chmod option
2189
        if (   (isset($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD]))
2190
		    && ($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD] != 0)) {
2191
 
2192
          // ----- Change the mode of the file
2193
          chmod($p_entry['filename'], $p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD]);
2194
        }
2195
 
2196
      }
2197
    }
2198
 
2199
    // ----- Look for post-extract callback
2200
    if (   (isset($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT]))
2201
	    && ($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT] != '')) {
2202
 
2203
      // ----- Generate a local information
2204
      $v_local_header = array();
2205
      $this->_convertHeader2FileInfo($p_entry, $v_local_header);
2206
 
2207
      // ----- Call the callback
2208
      // Here I do not use call_user_func() because I need to send a reference to the
2209
      // header.
2210
      eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT].'(ARCHIVE_ZIP_PARAM_POST_EXTRACT, $v_local_header);');
2211
    }
2212
 
2213
    // ----- Return
2214
    return $v_result;
2215
  }
2216
  // ---------------------------------------------------------------------------
2217
 
2218
  // ---------------------------------------------------------------------------
2219
  // Function : _extractFileAsString()
2220
  // Description :
2221
  // Parameters :
2222
  // Return Values :
2223
  // ---------------------------------------------------------------------------
2224
  /**
2225
  * Archive_Zip::_extractFileAsString()
2226
  *
2227
  * { Description }
2228
  *
2229
  */
2230
  function _extractFileAsString(&$p_entry, &$p_string)
2231
  {
2232
    $v_result=1;
2233
 
2234
    // ----- Read the file header
2235
    $v_header = array();
2236
    if (($v_result = $this->_readFileHeader($v_header)) != 1)
2237
    {
2238
      // ----- Return
2239
      return $v_result;
2240
    }
2241
 
2242
 
2243
    // ----- Check that the file header is coherent with $p_entry info
2244
    // TBC
2245
 
2246
    // ----- Trace
2247
 
2248
    // ----- Do the extraction (if not a folder)
2249
    if (!(($p_entry['external']&0x00000010)==0x00000010))
2250
    {
2251
      // ----- Look for not compressed file
2252
      if ($p_entry['compressed_size'] == $p_entry['size'])
2253
      {
2254
        // ----- Trace
2255
 
2256
        // ----- Reading the file
2257
        $p_string = fread($this->_zip_fd, $p_entry['compressed_size']);
2258
      }
2259
      else
2260
      {
2261
        // ----- Trace
2262
 
2263
        // ----- Reading the file
2264
        $v_data = fread($this->_zip_fd, $p_entry['compressed_size']);
2265
 
2266
        // ----- Decompress the file
2267
        $p_string = gzinflate($v_data);
2268
      }
2269
 
2270
      // ----- Trace
2271
    }
2272
    else {
2273
        // TBC : error : can not extract a folder in a string
2274
    }
2275
 
2276
    // ----- Return
2277
    return $v_result;
2278
  }
2279
  // ---------------------------------------------------------------------------
2280
 
2281
  // ---------------------------------------------------------------------------
2282
  // Function : _readFileHeader()
2283
  // Description :
2284
  // Parameters :
2285
  // Return Values :
2286
  // ---------------------------------------------------------------------------
2287
  /**
2288
  * Archive_Zip::_readFileHeader()
2289
  *
2290
  * { Description }
2291
  *
2292
  */
2293
  function _readFileHeader(&$p_header)
2294
  {
2295
    $v_result=1;
2296
 
2297
    // ----- Read the 4 bytes signature
2298
    $v_binary_data = @fread($this->_zip_fd, 4);
2299
    $v_data = unpack('Vid', $v_binary_data);
2300
 
2301
    // ----- Check signature
2302
    if ($v_data['id'] != 0x04034b50)
2303
    {
2304
 
2305
      // ----- Error log
2306
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
2307
 
2308
      // ----- Return
2309
      return Archive_Zip::errorCode();
2310
    }
2311
 
2312
    // ----- Read the first 42 bytes of the header
2313
    $v_binary_data = fread($this->_zip_fd, 26);
2314
 
2315
    // ----- Look for invalid block size
2316
    if (strlen($v_binary_data) != 26)
2317
    {
2318
      $p_header['filename'] = "";
2319
      $p_header['status'] = "invalid_header";
2320
 
2321
      // ----- Error log
2322
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
2323
 
2324
      // ----- Return
2325
      return Archive_Zip::errorCode();
2326
    }
2327
 
2328
    // ----- Extract the values
2329
    $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);
2330
 
2331
    // ----- Get filename
2332
    $p_header['filename'] = fread($this->_zip_fd, $v_data['filename_len']);
2333
 
2334
    // ----- Get extra_fields
2335
    if ($v_data['extra_len'] != 0) {
2336
      $p_header['extra'] = fread($this->_zip_fd, $v_data['extra_len']);
2337
    }
2338
    else {
2339
      $p_header['extra'] = '';
2340
    }
2341
 
2342
    // ----- Extract properties
2343
    $p_header['compression'] = $v_data['compression'];
2344
    $p_header['size'] = $v_data['size'];
2345
    $p_header['compressed_size'] = $v_data['compressed_size'];
2346
    $p_header['crc'] = $v_data['crc'];
2347
    $p_header['flag'] = $v_data['flag'];
2348
 
2349
    // ----- Recuperate date in UNIX format
2350
    $p_header['mdate'] = $v_data['mdate'];
2351
    $p_header['mtime'] = $v_data['mtime'];
2352
    if ($p_header['mdate'] && $p_header['mtime'])
2353
    {
2354
      // ----- Extract time
2355
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
2356
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
2357
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;
2358
 
2359
      // ----- Extract date
2360
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
2361
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
2362
      $v_day = $p_header['mdate'] & 0x001F;
2363
 
2364
      // ----- Get UNIX date format
2365
      $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
2366
 
2367
    }
2368
    else
2369
    {
2370
      $p_header['mtime'] = time();
2371
    }
2372
 
2373
    // ----- Other informations
2374
 
2375
    // TBC
2376
    //for(reset($v_data); $key = key($v_data); next($v_data)) {
2377
    //}
2378
 
2379
    // ----- Set the stored filename
2380
    $p_header['stored_filename'] = $p_header['filename'];
2381
 
2382
    // ----- Set the status field
2383
    $p_header['status'] = "ok";
2384
 
2385
    // ----- Return
2386
    return $v_result;
2387
  }
2388
  // ---------------------------------------------------------------------------
2389
 
2390
  // ---------------------------------------------------------------------------
2391
  // Function : _readCentralFileHeader()
2392
  // Description :
2393
  // Parameters :
2394
  // Return Values :
2395
  // ---------------------------------------------------------------------------
2396
  /**
2397
  * Archive_Zip::_readCentralFileHeader()
2398
  *
2399
  * { Description }
2400
  *
2401
  */
2402
  function _readCentralFileHeader(&$p_header)
2403
  {
2404
    $v_result=1;
2405
 
2406
    // ----- Read the 4 bytes signature
2407
    $v_binary_data = @fread($this->_zip_fd, 4);
2408
    $v_data = unpack('Vid', $v_binary_data);
2409
 
2410
    // ----- Check signature
2411
    if ($v_data['id'] != 0x02014b50)
2412
    {
2413
 
2414
      // ----- Error log
2415
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
2416
 
2417
      // ----- Return
2418
      return Archive_Zip::errorCode();
2419
    }
2420
 
2421
    // ----- Read the first 42 bytes of the header
2422
    $v_binary_data = fread($this->_zip_fd, 42);
2423
 
2424
    // ----- Look for invalid block size
2425
    if (strlen($v_binary_data) != 42)
2426
    {
2427
      $p_header['filename'] = "";
2428
      $p_header['status'] = "invalid_header";
2429
 
2430
      // ----- Error log
2431
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
2432
 
2433
      // ----- Return
2434
      return Archive_Zip::errorCode();
2435
    }
2436
 
2437
    // ----- Extract the values
2438
    $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);
2439
 
2440
    // ----- Get filename
2441
    if ($p_header['filename_len'] != 0)
2442
      $p_header['filename'] = fread($this->_zip_fd, $p_header['filename_len']);
2443
    else
2444
      $p_header['filename'] = '';
2445
 
2446
    // ----- Get extra
2447
    if ($p_header['extra_len'] != 0)
2448
      $p_header['extra'] = fread($this->_zip_fd, $p_header['extra_len']);
2449
    else
2450
      $p_header['extra'] = '';
2451
 
2452
    // ----- Get comment
2453
    if ($p_header['comment_len'] != 0)
2454
      $p_header['comment'] = fread($this->_zip_fd, $p_header['comment_len']);
2455
    else
2456
      $p_header['comment'] = '';
2457
 
2458
    // ----- Extract properties
2459
 
2460
    // ----- Recuperate date in UNIX format
2461
    if ($p_header['mdate'] && $p_header['mtime'])
2462
    {
2463
      // ----- Extract time
2464
      $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
2465
      $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
2466
      $v_seconde = ($p_header['mtime'] & 0x001F)*2;
2467
 
2468
      // ----- Extract date
2469
      $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
2470
      $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
2471
      $v_day = $p_header['mdate'] & 0x001F;
2472
 
2473
      // ----- Get UNIX date format
2474
      $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
2475
 
2476
    }
2477
    else
2478
    {
2479
      $p_header['mtime'] = time();
2480
    }
2481
 
2482
    // ----- Set the stored filename
2483
    $p_header['stored_filename'] = $p_header['filename'];
2484
 
2485
    // ----- Set default status to ok
2486
    $p_header['status'] = 'ok';
2487
 
2488
    // ----- Look if it is a directory
2489
    if (substr($p_header['filename'], -1) == '/')
2490
    {
2491
      $p_header['external'] = 0x41FF0010;
2492
    }
2493
 
2494
 
2495
    // ----- Return
2496
    return $v_result;
2497
  }
2498
  // ---------------------------------------------------------------------------
2499
 
2500
  // ---------------------------------------------------------------------------
2501
  // Function : _readEndCentralDir()
2502
  // Description :
2503
  // Parameters :
2504
  // Return Values :
2505
  // ---------------------------------------------------------------------------
2506
  /**
2507
  * Archive_Zip::_readEndCentralDir()
2508
  *
2509
  * { Description }
2510
  *
2511
  */
2512
  function _readEndCentralDir(&$p_central_dir)
2513
  {
2514
    $v_result=1;
2515
 
2516
    // ----- Go to the end of the zip file
2517
    $v_size = filesize($this->_zipname);
2518
    @fseek($this->_zip_fd, $v_size);
2519
    if (@ftell($this->_zip_fd) != $v_size) {
2520
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2521
	                   'Unable to go to the end of the archive \''
2522
					   .$this->_zipname.'\'');
2523
      return Archive_Zip::errorCode();
2524
    }
2525
 
2526
    // ----- First try : look if this is an archive with no commentaries
2527
	// (most of the time)
2528
    // in this case the end of central dir is at 22 bytes of the file end
2529
    $v_found = 0;
2530
    if ($v_size > 26) {
2531
      @fseek($this->_zip_fd, $v_size-22);
2532
      if (($v_pos = @ftell($this->_zip_fd)) != ($v_size-22)) {
2533
        $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2534
		                 'Unable to seek back to the middle of the archive \''
2535
						 .$this->_zipname.'\'');
2536
        return Archive_Zip::errorCode();
2537
      }
2538
 
2539
      // ----- Read for bytes
2540
      $v_binary_data = @fread($this->_zip_fd, 4);
2541
      $v_data = unpack('Vid', $v_binary_data);
2542
 
2543
      // ----- Check signature
2544
      if ($v_data['id'] == 0x06054b50) {
2545
        $v_found = 1;
2546
      }
2547
 
2548
      $v_pos = ftell($this->_zip_fd);
2549
    }
2550
 
2551
    // ----- Go back to the maximum possible size of the Central Dir End Record
2552
    if (!$v_found) {
2553
      $v_maximum_size = 65557; // 0xFFFF + 22;
2554
      if ($v_maximum_size > $v_size)
2555
        $v_maximum_size = $v_size;
2556
      @fseek($this->_zip_fd, $v_size-$v_maximum_size);
2557
      if (@ftell($this->_zip_fd) != ($v_size-$v_maximum_size)) {
2558
        $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2559
		                 'Unable to seek back to the middle of the archive \''
2560
						 .$this->_zipname.'\'');
2561
        return Archive_Zip::errorCode();
2562
      }
2563
 
2564
      // ----- Read byte per byte in order to find the signature
2565
      $v_pos = ftell($this->_zip_fd);
2566
      $v_bytes = 0x00000000;
2567
      while ($v_pos < $v_size) {
2568
        // ----- Read a byte
2569
        $v_byte = @fread($this->_zip_fd, 1);
2570
 
2571
        // -----  Add the byte
2572
        $v_bytes = ($v_bytes << 8) | Ord($v_byte);
2573
 
2574
        // ----- Compare the bytes
2575
        if ($v_bytes == 0x504b0506) {
2576
          $v_pos++;
2577
          break;
2578
        }
2579
 
2580
        $v_pos++;
2581
      }
2582
 
2583
      // ----- Look if not found end of central dir
2584
      if ($v_pos == $v_size) {
2585
        $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2586
		                 "Unable to find End of Central Dir Record signature");
2587
        return Archive_Zip::errorCode();
2588
      }
2589
    }
2590
 
2591
    // ----- Read the first 18 bytes of the header
2592
    $v_binary_data = fread($this->_zip_fd, 18);
2593
 
2594
    // ----- Look for invalid block size
2595
    if (strlen($v_binary_data) != 18) {
2596
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2597
	                   "Invalid End of Central Dir Record size : "
2598
					   .strlen($v_binary_data));
2599
      return Archive_Zip::errorCode();
2600
    }
2601
 
2602
    // ----- Extract the values
2603
    $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);
2604
 
2605
    // ----- Check the global size
2606
    if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {
2607
      $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
2608
	                   "Fail to find the right signature");
2609
      return Archive_Zip::errorCode();
2610
    }
2611
 
2612
    // ----- Get comment
2613
    if ($v_data['comment_size'] != 0)
2614
      $p_central_dir['comment'] = fread($this->_zip_fd, $v_data['comment_size']);
2615
    else
2616
      $p_central_dir['comment'] = '';
2617
 
2618
    $p_central_dir['entries'] = $v_data['entries'];
2619
    $p_central_dir['disk_entries'] = $v_data['disk_entries'];
2620
    $p_central_dir['offset'] = $v_data['offset'];
2621
    $p_central_dir['size'] = $v_data['size'];
2622
    $p_central_dir['disk'] = $v_data['disk'];
2623
    $p_central_dir['disk_start'] = $v_data['disk_start'];
2624
 
2625
    // ----- Return
2626
    return $v_result;
2627
  }
2628
  // ---------------------------------------------------------------------------
2629
 
2630
  // ---------------------------------------------------------------------------
2631
  // Function : _deleteByRule()
2632
  // Description :
2633
  // Parameters :
2634
  // Return Values :
2635
  // ---------------------------------------------------------------------------
2636
  /**
2637
  * Archive_Zip::_deleteByRule()
2638
  *
2639
  * { Description }
2640
  *
2641
  */
2642
  function _deleteByRule(&$p_result_list, &$p_params)
2643
  {
2644
    $v_result=1;
2645
    $v_list_detail = array();
2646
 
2647
    // ----- Open the zip file
2648
    if (($v_result=$this->_openFd('rb')) != 1)
2649
    {
2650
      // ----- Return
2651
      return $v_result;
2652
    }
2653
 
2654
    // ----- Read the central directory informations
2655
    $v_central_dir = array();
2656
    if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1)
2657
    {
2658
      $this->_closeFd();
2659
      return $v_result;
2660
    }
2661
 
2662
    // ----- Go to beginning of File
2663
    @rewind($this->_zip_fd);
2664
 
2665
    // ----- Scan all the files
2666
    // ----- Start at beginning of Central Dir
2667
    $v_pos_entry = $v_central_dir['offset'];
2668
    @rewind($this->_zip_fd);
2669
    if (@fseek($this->_zip_fd, $v_pos_entry)) {
2670
      // ----- Clean
2671
      $this->_closeFd();
2672
 
2673
      $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP,
2674
	                   'Invalid archive size');
2675
      return Archive_Zip::errorCode();
2676
    }
2677
 
2678
    // ----- Read each entry
2679
    $v_header_list = array();
2680
    $j_start = 0;
2681
    for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) {
2682
 
2683
      // ----- Read the file header
2684
      $v_header_list[$v_nb_extracted] = array();
2685
      $v_result
2686
	    = $this->_readCentralFileHeader($v_header_list[$v_nb_extracted]);
2687
      if ($v_result != 1) {
2688
        // ----- Clean
2689
        $this->_closeFd();
2690
 
2691
        return $v_result;
2692
      }
2693
 
2694
      // ----- Store the index
2695
      $v_header_list[$v_nb_extracted]['index'] = $i;
2696
 
2697
      // ----- Look for the specific extract rules
2698
      $v_found = false;
2699
 
2700
      // ----- Look for extract by name rule
2701
      if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_NAME]))
2702
          && ($p_params[ARCHIVE_ZIP_PARAM_BY_NAME] != 0)) {
2703
 
2704
          // ----- Look if the filename is in the list
2705
          for ($j=0;
2706
		       ($j<sizeof($p_params[ARCHIVE_ZIP_PARAM_BY_NAME]))
2707
			     && (!$v_found);
2708
			   $j++) {
2709
 
2710
              // ----- Look for a directory
2711
              if (substr($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j], -1) == "/") {
2712
 
2713
                  // ----- Look if the directory is in the filename path
2714
                  if (   (strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j]))
2715
                      && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j])) == $p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j])) {
2716
                      $v_found = true;
2717
                  }
2718
                  elseif (   (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */
2719
                          && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j])) {
2720
                      $v_found = true;
2721
                  }
2722
              }
2723
              // ----- Look for a filename
2724
              elseif ($v_header_list[$v_nb_extracted]['stored_filename']
2725
			          == $p_params[ARCHIVE_ZIP_PARAM_BY_NAME][$j]) {
2726
                  $v_found = true;
2727
              }
2728
          }
2729
      }
2730
 
2731
      // ----- Look for extract by ereg rule
2732
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_EREG]))
2733
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_EREG] != "")) {
2734
 
2735
          if (ereg($p_params[ARCHIVE_ZIP_PARAM_BY_EREG],
2736
		           $v_header_list[$v_nb_extracted]['stored_filename'])) {
2737
              $v_found = true;
2738
          }
2739
      }
2740
 
2741
      // ----- Look for extract by preg rule
2742
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_PREG]))
2743
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_PREG] != "")) {
2744
 
2745
          if (preg_match($p_params[ARCHIVE_ZIP_PARAM_BY_PREG],
2746
		                 $v_header_list[$v_nb_extracted]['stored_filename'])) {
2747
              $v_found = true;
2748
          }
2749
      }
2750
 
2751
      // ----- Look for extract by index rule
2752
      else if (   (isset($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX]))
2753
               && ($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX] != 0)) {
2754
 
2755
          // ----- Look if the index is in the list
2756
          for ($j=$j_start;
2757
		       ($j<sizeof($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX]))
2758
			     && (!$v_found);
2759
			   $j++) {
2760
 
2761
              if (   ($i>=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['start'])
2762
			      && ($i<=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['end'])) {
2763
                  $v_found = true;
2764
              }
2765
              if ($i>=$p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['end']) {
2766
                  $j_start = $j+1;
2767
              }
2768
 
2769
              if ($p_params[ARCHIVE_ZIP_PARAM_BY_INDEX][$j]['start']>$i) {
2770
                  break;
2771
              }
2772
          }
2773
      }
2774
 
2775
      // ----- Look for deletion
2776
      if ($v_found) {
2777
        unset($v_header_list[$v_nb_extracted]);
2778
      }
2779
      else {
2780
        $v_nb_extracted++;
2781
      }
2782
    }
2783
 
2784
    // ----- Look if something need to be deleted
2785
    if ($v_nb_extracted > 0) {
2786
 
2787
        // ----- Creates a temporay file
2788
        $v_zip_temp_name = ARCHIVE_ZIP_TEMPORARY_DIR.uniqid('archive_zip-')
2789
		                   .'.tmp';
2790
 
2791
        // ----- Creates a temporary zip archive
2792
        $v_temp_zip = new Archive_Zip($v_zip_temp_name);
2793
 
2794
        // ----- Open the temporary zip file in write mode
2795
        if (($v_result = $v_temp_zip->_openFd('wb')) != 1) {
2796
            $this->_closeFd();
2797
 
2798
            // ----- Return
2799
            return $v_result;
2800
        }
2801
 
2802
        // ----- Look which file need to be kept
2803
        for ($i=0; $i<sizeof($v_header_list); $i++) {
2804
 
2805
            // ----- Calculate the position of the header
2806
            @rewind($this->_zip_fd);
2807
            if (@fseek($this->_zip_fd,  $v_header_list[$i]['offset'])) {
2808
                // ----- Clean
2809
                $this->_closeFd();
2810
                $v_temp_zip->_closeFd();
2811
                @unlink($v_zip_temp_name);
2812
 
2813
                $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP,
2814
				                 'Invalid archive size');
2815
                return Archive_Zip::errorCode();
2816
            }
2817
 
2818
            // ----- Read the file header
2819
            if (($v_result = $this->_readFileHeader($v_header_list[$i])) != 1) {
2820
                // ----- Clean
2821
                $this->_closeFd();
2822
                $v_temp_zip->_closeFd();
2823
                @unlink($v_zip_temp_name);
2824
 
2825
                return $v_result;
2826
            }
2827
 
2828
            // ----- Write the file header
2829
            $v_result = $v_temp_zip->_writeFileHeader($v_header_list[$i]);
2830
            if ($v_result != 1) {
2831
                // ----- Clean
2832
                $this->_closeFd();
2833
                $v_temp_zip->_closeFd();
2834
                @unlink($v_zip_temp_name);
2835
 
2836
                return $v_result;
2837
            }
2838
 
2839
            // ----- Read/write the data block
2840
            $v_result = $this->_tool_CopyBlock($this->_zip_fd,
2841
			                                   $v_temp_zip->_zip_fd,
2842
								       $v_header_list[$i]['compressed_size']);
2843
            if ($v_result != 1) {
2844
                // ----- Clean
2845
                $this->_closeFd();
2846
                $v_temp_zip->_closeFd();
2847
                @unlink($v_zip_temp_name);
2848
 
2849
                return $v_result;
2850
            }
2851
        }
2852
 
2853
        // ----- Store the offset of the central dir
2854
        $v_offset = @ftell($v_temp_zip->_zip_fd);
2855
 
2856
        // ----- Re-Create the Central Dir files header
2857
        for ($i=0; $i<sizeof($v_header_list); $i++) {
2858
            // ----- Create the file header
2859
            $v_result=$v_temp_zip->_writeCentralFileHeader($v_header_list[$i]);
2860
            if ($v_result != 1) {
2861
            	// ----- Clean
2862
                $v_temp_zip->_closeFd();
2863
                $this->_closeFd();
2864
                @unlink($v_zip_temp_name);
2865
 
2866
                return $v_result;
2867
            }
2868
 
2869
            // ----- Transform the header to a 'usable' info
2870
            $v_temp_zip->_convertHeader2FileInfo($v_header_list[$i],
2871
			                                     $p_result_list[$i]);
2872
        }
2873
 
2874
 
2875
        // ----- Zip file comment
2876
        $v_comment = '';
2877
 
2878
        // ----- Calculate the size of the central header
2879
        $v_size = @ftell($v_temp_zip->_zip_fd)-$v_offset;
2880
 
2881
        // ----- Create the central dir footer
2882
        $v_result = $v_temp_zip->_writeCentralHeader(sizeof($v_header_list),
2883
		                                             $v_size, $v_offset,
2884
													 $v_comment);
2885
        if ($v_result != 1) {
2886
            // ----- Clean
2887
            unset($v_header_list);
2888
            $v_temp_zip->_closeFd();
2889
            $this->_closeFd();
2890
            @unlink($v_zip_temp_name);
2891
 
2892
            return $v_result;
2893
        }
2894
 
2895
        // ----- Close
2896
        $v_temp_zip->_closeFd();
2897
        $this->_closeFd();
2898
 
2899
        // ----- Delete the zip file
2900
        // TBC : I should test the result ...
2901
        @unlink($this->_zipname);
2902
 
2903
        // ----- Rename the temporary file
2904
        // TBC : I should test the result ...
2905
        //@rename($v_zip_temp_name, $this->_zipname);
2906
        $this->_tool_Rename($v_zip_temp_name, $this->_zipname);
2907
 
2908
        // ----- Destroy the temporary archive
2909
        unset($v_temp_zip);
2910
    }
2911
 
2912
    // ----- Return
2913
    return $v_result;
2914
  }
2915
  // ---------------------------------------------------------------------------
2916
 
2917
  // ---------------------------------------------------------------------------
2918
  // Function : _dirCheck()
2919
  // Description :
2920
  //   Check if a directory exists, if not it creates it and all the parents directory
2921
  //   which may be useful.
2922
  // Parameters :
2923
  //   $p_dir : Directory path to check.
2924
  // Return Values :
2925
  //    1 : OK
2926
  //   -1 : Unable to create directory
2927
  // ---------------------------------------------------------------------------
2928
  /**
2929
  * Archive_Zip::_dirCheck()
2930
  *
2931
  * { Description }
2932
  *
2933
  * @param [type] $p_is_dir
2934
  */
2935
  function _dirCheck($p_dir, $p_is_dir=false)
2936
  {
2937
    $v_result = 1;
2938
 
2939
    // ----- Remove the final '/'
2940
    if (($p_is_dir) && (substr($p_dir, -1)=='/')) {
2941
      $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
2942
    }
2943
 
2944
    // ----- Check the directory availability
2945
    if ((is_dir($p_dir)) || ($p_dir == "")) {
2946
      return 1;
2947
    }
2948
 
2949
    // ----- Extract parent directory
2950
    $p_parent_dir = dirname($p_dir);
2951
 
2952
    // ----- Just a check
2953
    if ($p_parent_dir != $p_dir) {
2954
      // ----- Look for parent directory
2955
      if ($p_parent_dir != "") {
2956
        if (($v_result = $this->_dirCheck($p_parent_dir)) != 1) {
2957
          return $v_result;
2958
        }
2959
      }
2960
    }
2961
 
2962
    // ----- Create the directory
2963
    if (!@mkdir($p_dir, 0777)) {
2964
      $this->_errorLog(ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL,
2965
	                   "Unable to create directory '$p_dir'");
2966
      return Archive_Zip::errorCode();
2967
    }
2968
 
2969
    // ----- Return
2970
    return $v_result;
2971
  }
2972
  // ---------------------------------------------------------------------------
2973
 
2974
  // ---------------------------------------------------------------------------
2975
  // Function : _merge()
2976
  // Description :
2977
  //   If $p_archive_to_add does not exist, the function exit with a success result.
2978
  // Parameters :
2979
  // Return Values :
2980
  // ---------------------------------------------------------------------------
2981
  /**
2982
  * Archive_Zip::_merge()
2983
  *
2984
  * { Description }
2985
  *
2986
  */
2987
  function _merge(&$p_archive_to_add)
2988
  {
2989
    $v_result=1;
2990
 
2991
    // ----- Look if the archive_to_add exists
2992
    if (!is_file($p_archive_to_add->_zipname)) {
2993
      // ----- Nothing to merge, so merge is a success
2994
      return 1;
2995
    }
2996
 
2997
    // ----- Look if the archive exists
2998
    if (!is_file($this->_zipname)) {
2999
      // ----- Do a duplicate
3000
      $v_result = $this->_duplicate($p_archive_to_add->_zipname);
3001
 
3002
      return $v_result;
3003
    }
3004
 
3005
    // ----- Open the zip file
3006
    if (($v_result=$this->_openFd('rb')) != 1) {
3007
      return $v_result;
3008
    }
3009
 
3010
    // ----- Read the central directory informations
3011
    $v_central_dir = array();
3012
    if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1) {
3013
      $this->_closeFd();
3014
      return $v_result;
3015
    }
3016
 
3017
    // ----- Go to beginning of File
3018
    @rewind($this->_zip_fd);
3019
 
3020
    // ----- Open the archive_to_add file
3021
    if (($v_result=$p_archive_to_add->_openFd('rb')) != 1) {
3022
      $this->_closeFd();
3023
      return $v_result;
3024
    }
3025
 
3026
    // ----- Read the central directory informations
3027
    $v_central_dir_to_add = array();
3028
    $v_result = $p_archive_to_add->_readEndCentralDir($v_central_dir_to_add);
3029
    if ($v_result != 1) {
3030
      $this->_closeFd();
3031
      $p_archive_to_add->_closeFd();
3032
      return $v_result;
3033
    }
3034
 
3035
    // ----- Go to beginning of File
3036
    @rewind($p_archive_to_add->_zip_fd);
3037
 
3038
    // ----- Creates a temporay file
3039
    $v_zip_temp_name = ARCHIVE_ZIP_TEMPORARY_DIR.uniqid('archive_zip-').'.tmp';
3040
 
3041
    // ----- Open the temporary file in write mode
3042
    if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) {
3043
      $this->_closeFd();
3044
      $p_archive_to_add->_closeFd();
3045
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
3046
	                   'Unable to open temporary file \''
3047
					   .$v_zip_temp_name.'\' in binary write mode');
3048
      return Archive_Zip::errorCode();
3049
    }
3050
 
3051
    // ----- Copy the files from the archive to the temporary file
3052
    // TBC : Here I should better append the file and go back to erase the
3053
	// central dir
3054
    $v_size = $v_central_dir['offset'];
3055
    while ($v_size != 0) {
3056
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3057
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3058
      $v_buffer = fread($this->_zip_fd, $v_read_size);
3059
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
3060
      $v_size -= $v_read_size;
3061
    }
3062
 
3063
    // ----- Copy the files from the archive_to_add into the temporary file
3064
    $v_size = $v_central_dir_to_add['offset'];
3065
    while ($v_size != 0) {
3066
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3067
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3068
      $v_buffer = fread($p_archive_to_add->_zip_fd, $v_read_size);
3069
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
3070
      $v_size -= $v_read_size;
3071
    }
3072
 
3073
    // ----- Store the offset of the central dir
3074
    $v_offset = @ftell($v_zip_temp_fd);
3075
 
3076
    // ----- Copy the block of file headers from the old archive
3077
    $v_size = $v_central_dir['size'];
3078
    while ($v_size != 0) {
3079
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3080
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3081
      $v_buffer = @fread($this->_zip_fd, $v_read_size);
3082
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
3083
      $v_size -= $v_read_size;
3084
    }
3085
 
3086
    // ----- Copy the block of file headers from the archive_to_add
3087
    $v_size = $v_central_dir_to_add['size'];
3088
    while ($v_size != 0) {
3089
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3090
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3091
      $v_buffer = @fread($p_archive_to_add->_zip_fd, $v_read_size);
3092
      @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size);
3093
      $v_size -= $v_read_size;
3094
    }
3095
 
3096
    // ----- Zip file comment
3097
    // TBC : I should merge the two comments
3098
    $v_comment = '';
3099
 
3100
    // ----- Calculate the size of the (new) central header
3101
    $v_size = @ftell($v_zip_temp_fd)-$v_offset;
3102
 
3103
    // ----- Swap the file descriptor
3104
    // Here is a trick : I swap the temporary fd with the zip fd, in order to use
3105
    // the following methods on the temporary fil and not the real archive fd
3106
    $v_swap = $this->_zip_fd;
3107
    $this->_zip_fd = $v_zip_temp_fd;
3108
    $v_zip_temp_fd = $v_swap;
3109
 
3110
    // ----- Create the central dir footer
3111
    if (($v_result = $this->_writeCentralHeader($v_central_dir['entries']
3112
	                                          +$v_central_dir_to_add['entries'],
3113
												$v_size, $v_offset,
3114
												$v_comment)) != 1) {
3115
      $this->_closeFd();
3116
      $p_archive_to_add->_closeFd();
3117
      @fclose($v_zip_temp_fd);
3118
      $this->_zip_fd = null;
3119
 
3120
      // ----- Reset the file list
3121
      unset($v_header_list);
3122
 
3123
      // ----- Return
3124
      return $v_result;
3125
    }
3126
 
3127
    // ----- Swap back the file descriptor
3128
    $v_swap = $this->_zip_fd;
3129
    $this->_zip_fd = $v_zip_temp_fd;
3130
    $v_zip_temp_fd = $v_swap;
3131
 
3132
    // ----- Close
3133
    $this->_closeFd();
3134
    $p_archive_to_add->_closeFd();
3135
 
3136
    // ----- Close the temporary file
3137
    @fclose($v_zip_temp_fd);
3138
 
3139
    // ----- Delete the zip file
3140
    // TBC : I should test the result ...
3141
    @unlink($this->_zipname);
3142
 
3143
    // ----- Rename the temporary file
3144
    // TBC : I should test the result ...
3145
    //@rename($v_zip_temp_name, $this->_zipname);
3146
    $this->_tool_Rename($v_zip_temp_name, $this->_zipname);
3147
 
3148
    // ----- Return
3149
    return $v_result;
3150
  }
3151
  // ---------------------------------------------------------------------------
3152
 
3153
  // ---------------------------------------------------------------------------
3154
  // Function : _duplicate()
3155
  // Description :
3156
  // Parameters :
3157
  // Return Values :
3158
  // ---------------------------------------------------------------------------
3159
  /**
3160
  * Archive_Zip::_duplicate()
3161
  *
3162
  * { Description }
3163
  *
3164
  */
3165
  function _duplicate($p_archive_filename)
3166
  {
3167
    $v_result=1;
3168
 
3169
    // ----- Look if the $p_archive_filename exists
3170
    if (!is_file($p_archive_filename)) {
3171
 
3172
      // ----- Nothing to duplicate, so duplicate is a success.
3173
      $v_result = 1;
3174
 
3175
      // ----- Return
3176
      return $v_result;
3177
    }
3178
 
3179
    // ----- Open the zip file
3180
    if (($v_result=$this->_openFd('wb')) != 1) {
3181
      // ----- Return
3182
      return $v_result;
3183
    }
3184
 
3185
    // ----- Open the temporary file in write mode
3186
    if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) {
3187
      $this->_closeFd();
3188
      $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
3189
	                   'Unable to open archive file \''
3190
					   .$p_archive_filename.'\' in binary write mode');
3191
      return Archive_Zip::errorCode();
3192
    }
3193
 
3194
    // ----- Copy the files from the archive to the temporary file
3195
    // TBC : Here I should better append the file and go back to erase the
3196
	// central dir
3197
    $v_size = filesize($p_archive_filename);
3198
    while ($v_size != 0) {
3199
      $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3200
	                  ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3201
      $v_buffer = fread($v_zip_temp_fd, $v_read_size);
3202
      @fwrite($this->_zip_fd, $v_buffer, $v_read_size);
3203
      $v_size -= $v_read_size;
3204
    }
3205
 
3206
    // ----- Close
3207
    $this->_closeFd();
3208
 
3209
    // ----- Close the temporary file
3210
    @fclose($v_zip_temp_fd);
3211
 
3212
    return $v_result;
3213
  }
3214
  // ---------------------------------------------------------------------------
3215
 
3216
  /**
3217
  * Archive_Zip::_check_parameters()
3218
  *
3219
  * { Description }
3220
  *
3221
  * @param integer $p_error_code
3222
  * @param string $p_error_string
3223
  */
3224
  function _check_parameters(&$p_params, $p_default)
3225
  {
3226
 
3227
    // ----- Check that param is an array
3228
    if (!is_array($p_params)) {
3229
        $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
3230
		                 'Unsupported parameter, waiting for an array');
3231
        return Archive_Zip::errorCode();
3232
    }
3233
 
3234
    // ----- Check that all the params are valid
3235
    for (reset($p_params); list($v_key, $v_value) = each($p_params); ) {
3236
    	if (!isset($p_default[$v_key])) {
3237
            $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
3238
			                 'Unsupported parameter with key \''.$v_key.'\'');
3239
 
3240
            return Archive_Zip::errorCode();
3241
    	}
3242
    }
3243
 
3244
	// ----- Set the default values
3245
    for (reset($p_default); list($v_key, $v_value) = each($p_default); ) {
3246
    	if (!isset($p_params[$v_key])) {
3247
    		$p_params[$v_key] = $p_default[$v_key];
3248
    	}
3249
    }
3250
 
3251
    // ----- Check specific parameters
3252
    $v_callback_list = array ('callback_pre_add','callback_post_add',
3253
	                          'callback_pre_extract','callback_post_extract');
3254
    for ($i=0; $i<sizeof($v_callback_list); $i++) {
3255
    	$v_key=$v_callback_list[$i];
3256
        if (   (isset($p_params[$v_key])) && ($p_params[$v_key] != '')) {
3257
            if (!function_exists($p_params[$v_key])) {
3258
                $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE,
3259
				                 "Callback '".$p_params[$v_key]
3260
								 ."()' is not an existing function for "
3261
								 ."parameter '".$v_key."'");
3262
                return Archive_Zip::errorCode();
3263
            }
3264
	    }
3265
    }
3266
 
3267
    return(1);
3268
  }
3269
  // ---------------------------------------------------------------------------
3270
 
3271
  // ---------------------------------------------------------------------------
3272
  // Function : _errorLog()
3273
  // Description :
3274
  // Parameters :
3275
  // ---------------------------------------------------------------------------
3276
  /**
3277
  * Archive_Zip::_errorLog()
3278
  *
3279
  * { Description }
3280
  *
3281
  * @param integer $p_error_code
3282
  * @param string $p_error_string
3283
  */
3284
  function _errorLog($p_error_code=0, $p_error_string='')
3285
  {
3286
      $this->_error_code = $p_error_code;
3287
      $this->_error_string = $p_error_string;
3288
  }
3289
  // ---------------------------------------------------------------------------
3290
 
3291
  // ---------------------------------------------------------------------------
3292
  // Function : _errorReset()
3293
  // Description :
3294
  // Parameters :
3295
  // ---------------------------------------------------------------------------
3296
  /**
3297
  * Archive_Zip::_errorReset()
3298
  *
3299
  * { Description }
3300
  *
3301
  */
3302
  function _errorReset()
3303
  {
3304
      $this->_error_code = 1;
3305
      $this->_error_string = '';
3306
  }
3307
  // ---------------------------------------------------------------------------
3308
 
3309
  // ---------------------------------------------------------------------------
3310
  // Function : $this->_tool_PathReduction()
3311
  // Description :
3312
  // Parameters :
3313
  // Return Values :
3314
  // ---------------------------------------------------------------------------
3315
  /**
3316
  * _tool_PathReduction()
3317
  *
3318
  * { Description }
3319
  *
3320
  */
3321
  function _tool_PathReduction($p_dir)
3322
  {
3323
    $v_result = "";
3324
 
3325
    // ----- Look for not empty path
3326
    if ($p_dir != "")
3327
    {
3328
      // ----- Explode path by directory names
3329
      $v_list = explode("/", $p_dir);
3330
 
3331
      // ----- Study directories from last to first
3332
      for ($i=sizeof($v_list)-1; $i>=0; $i--)
3333
      {
3334
        // ----- Look for current path
3335
        if ($v_list[$i] == ".")
3336
        {
3337
          // ----- Ignore this directory
3338
          // Should be the first $i=0, but no check is done
3339
        }
3340
        else if ($v_list[$i] == "..")
3341
        {
3342
          // ----- Ignore it and ignore the $i-1
3343
          $i--;
3344
        }
3345
        else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0))
3346
        {
3347
          // ----- Ignore only the double '//' in path,
3348
          // but not the first and last '/'
3349
        }
3350
        else
3351
        {
3352
          $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
3353
        }
3354
      }
3355
    }
3356
 
3357
    // ----- Return
3358
    return $v_result;
3359
  }
3360
  // ---------------------------------------------------------------------------
3361
 
3362
  // ---------------------------------------------------------------------------
3363
  // Function : $this->_tool_PathInclusion()
3364
  // Description :
3365
  //   This function indicates if the path $p_path is under the $p_dir tree. Or,
3366
  //   said in an other way, if the file or sub-dir $p_path is inside the dir
3367
  //   $p_dir.
3368
  //   The function indicates also if the path is exactly the same as the dir.
3369
  //   This function supports path with duplicated '/' like '//', but does not
3370
  //   support '.' or '..' statements.
3371
  // Parameters :
3372
  // Return Values :
3373
  //   0 if $p_path is not inside directory $p_dir
3374
  //   1 if $p_path is inside directory $p_dir
3375
  //   2 if $p_path is exactly the same as $p_dir
3376
  // ---------------------------------------------------------------------------
3377
  /**
3378
  * _tool_PathInclusion()
3379
  *
3380
  * { Description }
3381
  *
3382
  */
3383
  function _tool_PathInclusion($p_dir, $p_path)
3384
  {
3385
    $v_result = 1;
3386
 
3387
    // ----- Explode dir and path by directory separator
3388
    $v_list_dir = explode("/", $p_dir);
3389
    $v_list_dir_size = sizeof($v_list_dir);
3390
    $v_list_path = explode("/", $p_path);
3391
    $v_list_path_size = sizeof($v_list_path);
3392
 
3393
    // ----- Study directories paths
3394
    $i = 0;
3395
    $j = 0;
3396
    while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {
3397
 
3398
      // ----- Look for empty dir (path reduction)
3399
      if ($v_list_dir[$i] == '') {
3400
        $i++;
3401
        continue;
3402
      }
3403
      if ($v_list_path[$j] == '') {
3404
        $j++;
3405
        continue;
3406
      }
3407
 
3408
      // ----- Compare the items
3409
      if (   ($v_list_dir[$i] != $v_list_path[$j])
3410
	      && ($v_list_dir[$i] != '')
3411
		  && ( $v_list_path[$j] != ''))  {
3412
        $v_result = 0;
3413
      }
3414
 
3415
      // ----- Next items
3416
      $i++;
3417
      $j++;
3418
    }
3419
 
3420
    // ----- Look if everything seems to be the same
3421
    if ($v_result) {
3422
      // ----- Skip all the empty items
3423
      while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
3424
      while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;
3425
 
3426
      if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
3427
        // ----- There are exactly the same
3428
        $v_result = 2;
3429
      }
3430
      else if ($i < $v_list_dir_size) {
3431
        // ----- The path is shorter than the dir
3432
        $v_result = 0;
3433
      }
3434
    }
3435
 
3436
    // ----- Return
3437
    return $v_result;
3438
  }
3439
  // ---------------------------------------------------------------------------
3440
 
3441
  // ---------------------------------------------------------------------------
3442
  // Function : $this->_tool_CopyBlock()
3443
  // Description :
3444
  // Parameters :
3445
  //   $p_mode : read/write compression mode
3446
  //             0 : src & dest normal
3447
  //             1 : src gzip, dest normal
3448
  //             2 : src normal, dest gzip
3449
  //             3 : src & dest gzip
3450
  // Return Values :
3451
  // ---------------------------------------------------------------------------
3452
  /**
3453
  * _tool_CopyBlock()
3454
  *
3455
  * { Description }
3456
  *
3457
  * @param integer $p_mode
3458
  */
3459
  function _tool_CopyBlock($p_src, $p_dest, $p_size, $p_mode=0)
3460
  {
3461
    $v_result = 1;
3462
 
3463
    if ($p_mode==0)
3464
    {
3465
      while ($p_size != 0)
3466
      {
3467
        $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3468
		                ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3469
        $v_buffer = @fread($p_src, $v_read_size);
3470
        @fwrite($p_dest, $v_buffer, $v_read_size);
3471
        $p_size -= $v_read_size;
3472
      }
3473
    }
3474
    else if ($p_mode==1)
3475
    {
3476
      while ($p_size != 0)
3477
      {
3478
        $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3479
		                ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3480
        $v_buffer = @gzread($p_src, $v_read_size);
3481
        @fwrite($p_dest, $v_buffer, $v_read_size);
3482
        $p_size -= $v_read_size;
3483
      }
3484
    }
3485
    else if ($p_mode==2)
3486
    {
3487
      while ($p_size != 0)
3488
      {
3489
        $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3490
		                ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3491
        $v_buffer = @fread($p_src, $v_read_size);
3492
        @gzwrite($p_dest, $v_buffer, $v_read_size);
3493
        $p_size -= $v_read_size;
3494
      }
3495
    }
3496
    else if ($p_mode==3)
3497
    {
3498
      while ($p_size != 0)
3499
      {
3500
        $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
3501
		                ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
3502
        $v_buffer = @gzread($p_src, $v_read_size);
3503
        @gzwrite($p_dest, $v_buffer, $v_read_size);
3504
        $p_size -= $v_read_size;
3505
      }
3506
    }
3507
 
3508
    // ----- Return
3509
    return $v_result;
3510
  }
3511
  // ---------------------------------------------------------------------------
3512
 
3513
  // ---------------------------------------------------------------------------
3514
  // Function : $this->_tool_Rename()
3515
  // Description :
3516
  //   This function tries to do a simple rename() function. If it fails, it
3517
  //   tries to copy the $p_src file in a new $p_dest file and then unlink the
3518
  //   first one.
3519
  // Parameters :
3520
  //   $p_src : Old filename
3521
  //   $p_dest : New filename
3522
  // Return Values :
3523
  //   1 on success, 0 on failure.
3524
  // ---------------------------------------------------------------------------
3525
  /**
3526
  * _tool_Rename()
3527
  *
3528
  * { Description }
3529
  *
3530
  */
3531
  function _tool_Rename($p_src, $p_dest)
3532
  {
3533
    $v_result = 1;
3534
 
3535
    // ----- Try to rename the files
3536
    if (!@rename($p_src, $p_dest)) {
3537
 
3538
      // ----- Try to copy & unlink the src
3539
      if (!@copy($p_src, $p_dest)) {
3540
        $v_result = 0;
3541
      }
3542
      else if (!@unlink($p_src)) {
3543
        $v_result = 0;
3544
      }
3545
    }
3546
 
3547
    // ----- Return
3548
    return $v_result;
3549
  }
3550
  // ---------------------------------------------------------------------------
3551
 
3552
  // ---------------------------------------------------------------------------
3553
  // Function : $this->_tool_TranslateWinPath()
3554
  // Description :
3555
  //   Translate windows path by replacing '\' by '/' and optionally removing
3556
  //   drive letter.
3557
  // Parameters :
3558
  //   $p_path : path to translate.
3559
  //   $p_remove_disk_letter : true | false
3560
  // Return Values :
3561
  //   The path translated.
3562
  // ---------------------------------------------------------------------------
3563
  /**
3564
  * _tool_TranslateWinPath()
3565
  *
3566
  * { Description }
3567
  *
3568
  * @param [type] $p_remove_disk_letter
3569
  */
3570
  function _tool_TranslateWinPath($p_path, $p_remove_disk_letter=true)
3571
  {
3572
    if (stristr(php_uname(), 'windows')) {
3573
      // ----- Look for potential disk letter
3574
      if (   ($p_remove_disk_letter)
3575
	      && (($v_position = strpos($p_path, ':')) != false)) {
3576
          $p_path = substr($p_path, $v_position+1);
3577
      }
3578
      // ----- Change potential windows directory separator
3579
      if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
3580
          $p_path = strtr($p_path, '\\', '/');
3581
      }
3582
    }
3583
    return $p_path;
3584
  }
3585
  // ---------------------------------------------------------------------------
3586
 
3587
  }
3588
  // End of class
3589
 
3590