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