Rev Author Line No. Line
250 kaklik 1 <?PHP
2 /* $Id: unzip.lib.php,v 1.2 2006/01/17 17:02:31 cybot_tm Exp $ */
3  
4 /**
5 * ZIP file unpack classes. Contributed to the phpMyAdmin project.
6 *
7 * @category phpPublic
8 * @package File-Formats-ZIP
9 * @subpackage Unzip
10 * @filesource unzip.lib.php
11 * @version 1.0.1
12 *
13 * @author Holger Boskugel <vbwebprofi@gmx.de>
14 * @copyright Copyright © 2003, Holger Boskugel, Berlin, Germany
15 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
16 *
17 * @history
18 * 2003-12-02 - HB : Patched : naming bug : Time/Size of file
19 * Added : ZIP file comment
20 * Added : Check BZIP2 support of PHP
21 * 2003-11-29 - HB * Initial version
22 */
23  
24 /**
25 * Unzip class, which retrieves entries from ZIP files.
26 *
27 * Supports only the compression modes
28 * - 0 : Stored,
29 * - 8 : Deflated and
30 * - 12 : BZIP2
31 *
32 * Based on :<BR>
33 * <BR>
34 * {@link http://www.pkware.com/products/enterprise/white_papers/appnote.html
35 * * Official ZIP file format}<BR>
36 * {@link http://msdn.microsoft.com/library/en-us/w98ddk/hh/w98ddk/storage_5l4m.asp
37 * * Microsoft DOS date/time format}
38 *
39 * @category phpPublic
40 * @package File-Formats-ZIP
41 * @subpackage Unzip
42 * @version 1.0.1
43 * @author Holger Boskugel <vbwebprofi@gmx.de>
44 * @uses SimpleUnzipEntry
45 * @example example.unzip.php Two examples
46 */
47 class SimpleUnzip {
48 // 2003-12-02 - HB >
49 /**
50 * Array to store file entries
51 *
52 * @var string
53 * @access public
54 * @see ReadFile()
55 * @since 1.0.1
56 */
57 var $Comment = '';
58 // 2003-12-02 - HB <
59  
60 /**
61 * Array to store file entries
62 *
63 * @var array
64 * @access public
65 * @see ReadFile()
66 * @since 1.0
67 */
68 var $Entries = array();
69  
70 /**
71 * Name of the ZIP file
72 *
73 * @var string
74 * @access public
75 * @see ReadFile()
76 * @since 1.0
77 */
78 var $Name = '';
79  
80 /**
81 * Size of the ZIP file
82 *
83 * @var integer
84 * @access public
85 * @see ReadFile()
86 * @since 1.0
87 */
88 var $Size = 0;
89  
90 /**
91 * Time of the ZIP file (unix timestamp)
92 *
93 * @var integer
94 * @access public
95 * @see ReadFile()
96 * @since 1.0
97 */
98 var $Time = 0;
99  
100 /**
101 * Contructor of the class
102 *
103 * @param string File name
104 * @return SimpleUnzip Instanced class
105 * @access public
106 * @uses SimpleUnzip::ReadFile() Opens file on new if specified
107 * @since 1.0
108 */
109 function SimpleUnzip($in_FileName = '')
110 {
111 if ($in_FileName !== '') {
112 SimpleUnzip::ReadFile($in_FileName);
113 }
114 } // end of the 'SimpleUnzip' constructor
115  
116 /**
117 * Counts the entries
118 *
119 * @return integer Count of ZIP entries
120 * @access public
121 * @uses $Entries
122 * @since 1.0
123 */
124 function Count()
125 {
126 return count($this->Entries);
127 } // end of the 'Count()' method
128  
129 /**
130 * Gets data of the specified ZIP entry
131 *
132 * @param integer Index of the ZIP entry
133 * @return mixed Data for the ZIP entry
134 * @uses SimpleUnzipEntry::$Data
135 * @access public
136 * @since 1.0
137 */
138 function GetData($in_Index)
139 {
140 return $this->Entries[$in_Index]->Data;
141 } // end of the 'GetData()' method
142  
143 /**
144 * Gets an entry of the ZIP file
145 *
146 * @param integer Index of the ZIP entry
147 * @return SimpleUnzipEntry Entry of the ZIP file
148 * @uses $Entries
149 * @access public
150 * @since 1.0
151 */
152 function GetEntry($in_Index)
153 {
154 return $this->Entries[$in_Index];
155 } // end of the 'GetEntry()' method
156  
157 /**
158 * Gets error code for the specified ZIP entry
159 *
160 * @param integer Index of the ZIP entry
161 * @return integer Error code for the ZIP entry
162 * @uses SimpleUnzipEntry::$Error
163 * @access public
164 * @since 1.0
165 */
166 function GetError($in_Index)
167 {
168 return $this->Entries[$in_Index]->Error;
169 } // end of the 'GetError()' method
170  
171 /**
172 * Gets error message for the specified ZIP entry
173 *
174 * @param integer Index of the ZIP entry
175 * @return string Error message for the ZIP entry
176 * @uses SimpleUnzipEntry::$ErrorMsg
177 * @access public
178 * @since 1.0
179 */
180 function GetErrorMsg($in_Index)
181 {
182 return $this->Entries[$in_Index]->ErrorMsg;
183 } // end of the 'GetErrorMsg()' method
184  
185 /**
186 * Gets file name for the specified ZIP entry
187 *
188 * @param integer Index of the ZIP entry
189 * @return string File name for the ZIP entry
190 * @uses SimpleUnzipEntry::$Name
191 * @access public
192 * @since 1.0
193 */
194 function GetName($in_Index)
195 {
196 return $this->Entries[$in_Index]->Name;
197 } // end of the 'GetName()' method
198  
199 /**
200 * Gets path of the file for the specified ZIP entry
201 *
202 * @param integer Index of the ZIP entry
203 * @return string Path of the file for the ZIP entry
204 * @uses SimpleUnzipEntry::$Path
205 * @access public
206 * @since 1.0
207 */
208 function GetPath($in_Index)
209 {
210 return $this->Entries[$in_Index]->Path;
211 } // end of the 'GetPath()' method
212  
213 /**
214 * Gets file time for the specified ZIP entry
215 *
216 * @param integer Index of the ZIP entry
217 * @return integer File time for the ZIP entry (unix timestamp)
218 * @uses SimpleUnzipEntry::$Time
219 * @access public
220 * @since 1.0
221 */
222 function GetTime($in_Index)
223 {
224 return $this->Entries[$in_Index]->Time;
225 } // end of the 'GetTime()' method
226  
227 /**
228 * Reads ZIP file and extracts the entries
229 *
230 * @param string File name of the ZIP archive
231 * @return array ZIP entry list (see also class variable {@link $Entries $Entries})
232 * @uses SimpleUnzipEntry For the entries
233 * @access public
234 * @since 1.0
235 */
236 function ReadFile($in_FileName)
237 {
238 $this->Entries = array();
239  
240 // Get file parameters
241 $this->Name = $in_FileName;
242 $this->Time = filemtime($in_FileName);
243 $this->Size = filesize($in_FileName);
244  
245 // Read file
246 $oF = fopen($in_FileName, 'rb');
247 $vZ = fread($oF, $this->Size);
248 fclose($oF);
249  
250 // 2003-12-02 - HB >
251 // Cut end of central directory
252 $aE = explode("\x50\x4b\x05\x06", $vZ);
253  
254 // Easiest way, but not sure if format changes
255 //$this->Comment = substr($aE[1], 18);
256  
257 // Normal way
258 $aP = unpack('x16/v1CL', $aE[1]);
259 $this->Comment = substr($aE[1], 18, $aP['CL']);
260  
261 // Translates end of line from other operating systems
262 $this->Comment = strtr($this->Comment, array("\r\n" => "\n",
263 "\r" => "\n"));
264 // 2003-12-02 - HB <
265  
266 // Cut the entries from the central directory
267 $aE = explode("\x50\x4b\x01\x02", $vZ);
268 // Explode to each part
269 $aE = explode("\x50\x4b\x03\x04", $aE[0]);
270 // Shift out spanning signature or empty entry
271 array_shift($aE);
272  
273 // Loop through the entries
274 foreach ($aE as $vZ) {
275 $aI = array();
276 $aI['E'] = 0;
277 $aI['EM'] = '';
278 // Retrieving local file header information
279 $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ);
280 // Check if data is encrypted
281 $bE = ($aP['GPF'] && 0x0001) ? TRUE : FALSE;
282 $nF = $aP['FNL'];
283  
284 // Special case : value block after the compressed data
285 if ($aP['GPF'] & 0x0008) {
286 $aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12));
287  
288 $aP['CRC'] = $aP1['CRC'];
289 $aP['CS'] = $aP1['CS'];
290 $aP['UCS'] = $aP1['UCS'];
291  
292 $vZ = substr($vZ, 0, -12);
293 }
294  
295 // Getting stored filename
296 $aI['N'] = substr($vZ, 26, $nF);
297  
298 if (substr($aI['N'], -1) == '/') {
299 // is a directory entry - will be skipped
300 continue;
301 }
302  
303 // Truncate full filename in path and filename
304 $aI['P'] = dirname($aI['N']);
305 $aI['P'] = $aI['P'] == '.' ? '' : $aI['P'];
306 $aI['N'] = basename($aI['N']);
307  
308 $vZ = substr($vZ, 26 + $nF);
309  
310 if (strlen($vZ) != $aP['CS']) {
311 $aI['E'] = 1;
312 $aI['EM'] = 'Compressed size is not equal with the value in header information.';
313 } else {
314 if ($bE) {
315 $aI['E'] = 5;
316 $aI['EM'] = 'File is encrypted, which is not supported from this class.';
317 } else {
318 switch($aP['CM']) {
319 case 0: // Stored
320 // Here is nothing to do, the file ist flat.
321 break;
322  
323 case 8: // Deflated
324 $vZ = gzinflate($vZ);
325 break;
326  
327 case 12: // BZIP2
328 // 2003-12-02 - HB >
329 if (! extension_loaded('bz2')) {
330 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
331 @dl('php_bz2.dll');
332 } else {
333 @dl('bz2.so');
334 }
335 }
336  
337 if (extension_loaded('bz2')) {
338 // 2003-12-02 - HB <
339 $vZ = bzdecompress($vZ);
340 // 2003-12-02 - HB >
341 } else {
342 $aI['E'] = 7;
343 $aI['EM'] = "PHP BZIP2 extension not available.";
344 }
345 // 2003-12-02 - HB <
346  
347 break;
348  
349 default:
350 $aI['E'] = 6;
351 $aI['EM'] = "De-/Compression method {$aP['CM']} is not supported.";
352 }
353  
354 // 2003-12-02 - HB >
355 if (! $aI['E']) {
356 // 2003-12-02 - HB <
357 if ($vZ === FALSE) {
358 $aI['E'] = 2;
359 $aI['EM'] = 'Decompression of data failed.';
360 } else {
361 if (strlen($vZ) != $aP['UCS']) {
362 $aI['E'] = 3;
363 $aI['EM'] = 'Uncompressed size is not equal with the value in header information.';
364 } else {
365 if (crc32($vZ) != $aP['CRC']) {
366 $aI['E'] = 4;
367 $aI['EM'] = 'CRC32 checksum is not equal with the value in header information.';
368 }
369 }
370 }
371 // 2003-12-02 - HB >
372 }
373 // 2003-12-02 - HB <
374 }
375 }
376  
377 $aI['D'] = $vZ;
378  
379 // DOS to UNIX timestamp
380 $aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11,
381 ($aP['FT'] & 0x07e0) >> 5,
382 ($aP['FT'] & 0x001f) << 1,
383 ($aP['FD'] & 0x01e0) >> 5,
384 ($aP['FD'] & 0x001f),
385 (($aP['FD'] & 0xfe00) >> 9) + 1980);
386  
387 $this->Entries[] = &new SimpleUnzipEntry($aI);
388 } // end for each entries
389  
390 return $this->Entries;
391 } // end of the 'ReadFile()' method
392 } // end of the 'SimpleUnzip' class
393  
394 /**
395 * Entry of the ZIP file.
396 *
397 * @category phpPublic
398 * @package File-Formats-ZIP
399 * @subpackage Unzip
400 * @version 1.0
401 * @author Holger Boskugel <vbwebprofi@gmx.de>
402 * @example example.unzip.php Two examples
403 */
404 class SimpleUnzipEntry {
405 /**
406 * Data of the file entry
407 *
408 * @var mixed
409 * @access public
410 * @see SimpleUnzipEntry()
411 * @since 1.0
412 */
413 var $Data = '';
414  
415 /**
416 * Error of the file entry
417 *
418 * - 0 : No error raised.<BR>
419 * - 1 : Compressed size is not equal with the value in header information.<BR>
420 * - 2 : Decompression of data failed.<BR>
421 * - 3 : Uncompressed size is not equal with the value in header information.<BR>
422 * - 4 : CRC32 checksum is not equal with the value in header information.<BR>
423 * - 5 : File is encrypted, which is not supported from this class.<BR>
424 * - 6 : De-/Compression method ... is not supported.<BR>
425 * - 7 : PHP BZIP2 extension not available.
426 *
427 * @var integer
428 * @access public
429 * @see SimpleUnzipEntry()
430 * @since 1.0
431 */
432 var $Error = 0;
433  
434 /**
435 * Error message of the file entry
436 *
437 * @var string
438 * @access public
439 * @see SimpleUnzipEntry()
440 * @since 1.0
441 */
442 var $ErrorMsg = '';
443  
444 /**
445 * File name of the file entry
446 *
447 * @var string
448 * @access public
449 * @see SimpleUnzipEntry()
450 * @since 1.0
451 */
452 var $Name = '';
453  
454 /**
455 * File path of the file entry
456 *
457 * @var string
458 * @access public
459 * @see SimpleUnzipEntry()
460 * @since 1.0
461 */
462 var $Path = '';
463  
464 /**
465 * File time of the file entry (unix timestamp)
466 *
467 * @var integer
468 * @access public
469 * @see SimpleUnzipEntry()
470 * @since 1.0
471 */
472 var $Time = 0;
473  
474 /**
475 * Contructor of the class
476 *
477 * @param array Entry datas
478 * @return SimpleUnzipEntry Instanced class
479 * @access public
480 * @since 1.0
481 */
482 function SimpleUnzipEntry($in_Entry)
483 {
484 $this->Data = $in_Entry['D'];
485 $this->Error = $in_Entry['E'];
486 $this->ErrorMsg = $in_Entry['EM'];
487 $this->Name = $in_Entry['N'];
488 $this->Path = $in_Entry['P'];
489 $this->Time = $in_Entry['T'];
490 } // end of the 'SimpleUnzipEntry' constructor
491 } // end of the 'SimpleUnzipEntry' class
492 ?>