Creating repository for dokuwiki modifications for sudaraka.org
[sudaraka-org:dokuwiki-mods.git] / inc / ZipLib.class.php
1 <?php
2
3 /**
4  * @author     bouchon
5  * @link       http://dev.maxg.info
6  * @link       http://forum.maxg.info
7  *
8  * Modified for Dokuwiki
9  * @author    Christopher Smith <chris@jalakai.co.uk>
10  */
11 class ZipLib {
12
13     var $datasec;
14     var $ctrl_dir = array();
15     var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
16     var $old_offset = 0;
17     var $dirs = Array(".");
18
19     function get_List($zip_name) {
20         $zip = @fopen($zip_name, 'rb');
21         if(!$zip) return(0);
22         $centd = $this->ReadCentralDir($zip,$zip_name);
23
24         @rewind($zip);
25         @fseek($zip, $centd['offset']);
26
27         for ($i=0; $i<$centd['entries']; $i++) {
28             $header = $this->ReadCentralFileHeaders($zip);
29             $header['index'] = $i;
30
31             $info['filename']        = $header['filename'];
32             $info['stored_filename'] = $header['stored_filename'];
33             $info['size']            = $header['size'];
34             $info['compressed_size'] = $header['compressed_size'];
35             $info['crc']             = strtoupper(dechex( $header['crc'] ));
36             $info['mtime']           = $header['mtime'];
37             $info['comment']         = $header['comment'];
38             $info['folder']          = ($header['external']==0x41FF0010||$header['external']==16)?1:0;
39             $info['index']           = $header['index'];
40             $info['status']          = $header['status'];
41             $ret[]=$info;
42
43             unset($header);
44         }
45         return $ret;
46     }
47
48     function Add($files,$compact) {
49         if(!is_array($files[0])) $files=Array($files);
50
51         for($i=0;$files[$i];$i++){
52             $fn = $files[$i];
53             if(!in_Array(dirname($fn[0]),$this->dirs))
54                 $this->add_Dir(dirname($fn[0]));
55             if(basename($fn[0]))
56                 $ret[basename($fn[0])]=$this->add_File($fn[1],$fn[0],$compact);
57         }
58         return $ret;
59     }
60
61     /**
62      * Zips recursively the $folder directory, from the $basedir directory
63      */
64     function Compress($folder, $basedir=null, $parent=null) {
65         $full_path = $basedir."/".$parent.$folder;
66         $zip_path = $parent.$folder;
67         if ($zip_path) {
68             $zip_path .= "/";
69             $this->add_dir($zip_path);
70         }
71         $dir = new DirectoryIterator($full_path);
72         foreach($dir as $file) {
73             if(!$file->isDot()) {
74                 $filename = $file->getFilename();
75                 if($file->isDir()) {
76                     $this->Compress($filename, $basedir, $zip_path);
77                 } else {
78                     $content = join('', file($full_path.'/'.$filename));
79                     $this->add_File($content, $zip_path.$filename);
80                 }
81             }
82         }
83     }
84
85     /**
86      * Returns the Zip file
87      */
88     function get_file() {
89         $data = implode('', $this -> datasec);
90         $ctrldir = implode('', $this -> ctrl_dir);
91
92         return $data . $ctrldir . $this -> eof_ctrl_dir .
93             pack('v', count($this->ctrl_dir)).pack('v', count($this->ctrl_dir)).
94             pack('V', strlen($ctrldir)) . pack('V', strlen($data)) . "\x00\x00";
95     }
96
97     function add_dir($name) {
98         $name = str_replace("\\", "/", $name);
99         $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";
100
101         $fr .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) );
102         $fr .= pack("v", 0 ).$name.pack("V", 0).pack("V", 0).pack("V", 0);
103         $this -> datasec[] = $fr;
104
105         $new_offset = strlen(implode("", $this->datasec));
106
107         $cdrec = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00";
108         $cdrec .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) );
109         $cdrec .= pack("v", 0 ).pack("v", 0 ).pack("v", 0 ).pack("v", 0 );
110         $ext = "\xff\xff\xff\xff";
111         $cdrec .= pack("V", 16 ).pack("V", $this -> old_offset ).$name;
112
113         $this -> ctrl_dir[] = $cdrec;
114         $this -> old_offset = $new_offset;
115         $this -> dirs[] = $name;
116     }
117
118     /**
119      * Add a file named $name from a string $data
120      */
121     function add_File($data, $name, $compact = 1) {
122         $name     = str_replace('\\', '/', $name);
123         $dtime    = dechex($this->DosTime());
124
125         $hexdtime = pack('H*',$dtime[6].$dtime[7].
126                               $dtime[4].$dtime[5].
127                               $dtime[2].$dtime[3].
128                               $dtime[0].$dtime[1]);
129
130         if($compact){
131             $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00".$hexdtime;
132         }else{
133             $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00".$hexdtime;
134         }
135         $unc_len = strlen($data);
136         $crc = crc32($data);
137
138         if($compact){
139             $zdata = gzcompress($data);
140             $c_len = strlen($zdata);
141             $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
142         }else{
143             $zdata = $data;
144         }
145         $c_len=strlen($zdata);
146         $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len);
147         $fr .= pack('v', strlen($name)).pack('v', 0).$name.$zdata;
148
149         $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len);
150
151         $this -> datasec[] = $fr;
152         $new_offset        = strlen(implode('', $this->datasec));
153         if($compact) {
154             $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00";
155         } else {
156             $cdrec = "\x50\x4b\x01\x02\x14\x00\x0a\x00\x00\x00\x00\x00";
157         }
158         $cdrec .= $hexdtime.pack('V', $crc).pack('V', $c_len).pack('V', $unc_len);
159         $cdrec .= pack('v', strlen($name) ).pack('v', 0 ).pack('v', 0 );
160         $cdrec .= pack('v', 0 ).pack('v', 0 ).pack('V', 32 );
161         $cdrec .= pack('V', $this -> old_offset );
162
163         $this -> old_offset = $new_offset;
164         $cdrec .= $name;
165         $this -> ctrl_dir[] = $cdrec;
166         return true;
167     }
168
169     function DosTime() {
170         $timearray = getdate();
171         if ($timearray['year'] < 1980) {
172             $timearray['year']    = 1980;
173             $timearray['mon']     = 1;
174             $timearray['mday']    = 1;
175             $timearray['hours']   = 0;
176             $timearray['minutes'] = 0;
177             $timearray['seconds'] = 0;
178         }
179         return (($timearray['year'] - 1980) << 25) |
180                 ($timearray['mon'] << 21) |
181                 ($timearray['mday'] << 16) |
182                 ($timearray['hours'] << 11) |
183                 ($timearray['minutes'] << 5) |
184                 ($timearray['seconds'] >> 1);
185     }
186
187     /**
188      * Extract a zip file $zn to the $to directory
189      */
190     function Extract ( $zn, $to, $index = Array(-1) ) {
191         if(!@is_dir($to)) $this->_mkdir($to);
192         $ok = 0;
193         $zip = @fopen($zn,'rb');
194         if(!$zip) return(-1);
195         $cdir = $this->ReadCentralDir($zip,$zn);
196         $pos_entry = $cdir['offset'];
197
198         if(!is_array($index)){
199             $index = array($index);
200         }
201         for($i=0; isset($index[$i]);$i++){
202             if(intval($index[$i])!=$index[$i]||$index[$i]>$cdir['entries'])
203                 return(-1);
204         }
205
206         for ($i=0; $i<$cdir['entries']; $i++) {
207             @fseek($zip, $pos_entry);
208             $header = $this->ReadCentralFileHeaders($zip);
209             $header['index'] = $i;
210             $pos_entry = ftell($zip);
211             @rewind($zip);
212             fseek($zip, $header['offset']);
213             if(in_array("-1",$index)||in_array($i,$index)){
214                 $stat[$header['filename']]=$this->ExtractFile($header, $to, $zip);
215             }
216         }
217         fclose($zip);
218         return $stat;
219     }
220
221     function ReadFileHeader($zip, $header) {
222         $binary_data = fread($zip, 30);
223         $data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data);
224
225         $header['filename'] = fread($zip, $data['filename_len']);
226         if ($data['extra_len'] != 0) {
227             $header['extra'] = fread($zip, $data['extra_len']);
228         } else {
229             $header['extra'] = '';
230         }
231
232         $header['compression'] = $data['compression'];
233         foreach (array('size','compressed_size','crc') as $hd) { // On ODT files, these headers are 0. Keep the previous value.
234             if ($data[$hd] != 0) $header[$hd] = $data[$hd];
235         }
236         $header['flag']  = $data['flag'];
237         $header['mdate'] = $data['mdate'];
238         $header['mtime'] = $data['mtime'];
239
240         if ($header['mdate'] && $header['mtime']){
241             $hour    = ($header['mtime']&0xF800)>>11;
242             $minute  = ($header['mtime']&0x07E0)>>5;
243             $seconde = ($header['mtime']&0x001F)*2;
244             $year    = (($header['mdate']&0xFE00)>>9)+1980;
245             $month   = ($header['mdate']&0x01E0)>>5;
246             $day     = $header['mdate']&0x001F;
247             $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
248         } else {
249             $header['mtime'] = time();
250         }
251
252         $header['stored_filename'] = $header['filename'];
253         $header['status'] = "ok";
254         return $header;
255     }
256
257     function ReadCentralFileHeaders($zip){
258         $binary_data = fread($zip, 46);
259         $header = unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data);
260
261         if ($header['filename_len'] != 0){
262             $header['filename'] = fread($zip,$header['filename_len']);
263         }else{
264             $header['filename'] = '';
265         }
266
267         if ($header['extra_len'] != 0){
268             $header['extra'] = fread($zip, $header['extra_len']);
269         }else{
270             $header['extra'] = '';
271         }
272
273         if ($header['comment_len'] != 0){
274             $header['comment'] = fread($zip, $header['comment_len']);
275         }else{
276             $header['comment'] = '';
277         }
278
279         if ($header['mdate'] && $header['mtime']) {
280             $hour    = ($header['mtime'] & 0xF800) >> 11;
281             $minute  = ($header['mtime'] & 0x07E0) >> 5;
282             $seconde = ($header['mtime'] & 0x001F)*2;
283             $year    = (($header['mdate'] & 0xFE00) >> 9) + 1980;
284             $month   = ($header['mdate'] & 0x01E0) >> 5;
285             $day     = $header['mdate'] & 0x001F;
286             $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year);
287         } else {
288             $header['mtime'] = time();
289         }
290
291         $header['stored_filename'] = $header['filename'];
292         $header['status'] = 'ok';
293         if (substr($header['filename'], -1) == '/') $header['external'] = 0x41FF0010;
294
295         return $header;
296     }
297
298     function ReadCentralDir($zip,$zip_name) {
299         $size = filesize($zip_name);
300         if ($size < 277){
301             $maximum_size = $size;
302         } else {
303             $maximum_size=277;
304         }
305
306         @fseek($zip, $size-$maximum_size);
307         $pos   = ftell($zip);
308         $bytes = 0x00000000;
309
310         while ($pos < $size) {
311             $byte = @fread($zip, 1);
312             $bytes=(($bytes << 8) & 0xFFFFFFFF) | Ord($byte);
313             if ($bytes == 0x504b0506){
314                 $pos++;
315                 break;
316             }
317             $pos++;
318         }
319
320         $data=unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size',
321                 fread($zip, 18));
322
323         if ($data['comment_size'] != 0){
324             $centd['comment'] = fread($zip, $data['comment_size']);
325         } else {
326             $centd['comment'] = '';
327         }
328         $centd['entries']      = $data['entries'];
329         $centd['disk_entries'] = $data['disk_entries'];
330         $centd['offset']       = $data['offset'];
331         $centd['disk_start']   = $data['disk_start'];
332         $centd['size']         = $data['size'];
333         $centd['disk']         = $data['disk'];
334         return $centd;
335     }
336
337     function ExtractFile($header,$to,$zip) {
338         $header = $this->readfileheader($zip, $header);
339
340         if(substr($to,-1)!="/") $to.="/";
341         if(substr($header['filename'],-1)=="/") {
342             $this->_mkdir($to.$header['filename']);
343             return +2;
344         }
345
346         if (!$this->_mkdir($to.dirname($header['filename']))) return (-1);
347
348         if (!array_key_exists("external", $header) || (!($header['external']==0x41FF0010)&&!($header['external']==16))) {
349             if ($header['compression']==0) {
350                 $fp = @fopen($to.$header['filename'], 'wb');
351                 if(!$fp) return(-1);
352                 $size = $header['compressed_size'];
353
354                 while ($size != 0) {
355                     $read_size = ($size < 2048 ? $size : 2048);
356                     $buffer = fread($zip, $read_size);
357                     $binary_data = pack('a'.$read_size, $buffer);
358                     @fwrite($fp, $binary_data, $read_size);
359                     $size -= $read_size;
360                 }
361                 fclose($fp);
362                 touch($to.$header['filename'], $header['mtime']);
363
364             }else{
365                 if (!is_dir(dirname($to.$header['filename']))) $this->_mkdir(dirname($to.$header['filename']));
366                 $fp = fopen($to.$header['filename'].'.gz','wb');
367                 if(!$fp) return(-1);
368                 $binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($header['compression']),
369                         Chr(0x00), time(), Chr(0x00), Chr(3));
370
371                 fwrite($fp, $binary_data, 10);
372                 $size = $header['compressed_size'];
373
374                 while ($size != 0) {
375                     $read_size = ($size < 1024 ? $size : 1024);
376                     $buffer = fread($zip, $read_size);
377                     $binary_data = pack('a'.$read_size, $buffer);
378                     @fwrite($fp, $binary_data, $read_size);
379                     $size -= $read_size;
380                 }
381
382                 $binary_data = pack('VV', $header['crc'], $header['size']);
383                 fwrite($fp, $binary_data,8);
384                 fclose($fp);
385
386                 $gzp = @gzopen($to.$header['filename'].'.gz','rb');
387                 if(!$gzp){
388                     @gzclose($gzp);
389                     @unlink($to.$header['filename']);
390                     die("Archive is compressed whereas ZLIB is not enabled.");
391                 }
392                 $fp = @fopen($to.$header['filename'],'wb');
393                 if(!$fp) return(-1);
394                 $size = $header['size'];
395
396                 while ($size != 0) {
397                     $read_size   = ($size < 2048 ? $size : 2048);
398                     $buffer      = gzread($gzp, $read_size);
399                     $binary_data = pack('a'.$read_size, $buffer);
400                     @fwrite($fp, $binary_data, $read_size);
401                     $size -= $read_size;
402                 }
403                 fclose($fp);
404                 gzclose($gzp);
405
406                 touch($to.$header['filename'], $header['mtime']);
407                 @unlink($to.$header['filename'].'.gz');
408             }
409         }
410         return true;
411     }
412
413     /**
414      * centralize mkdir calls and use dokuwiki io functions
415      *
416      * @author Christopher Smith <chris@jalakai.co.uk>
417      */
418     function _mkdir($d) {
419         return io_mkdir_p($d);
420     }
421
422
423     function ExtractStr($zn, $name) {
424         $ok = 0;
425         $zip = @fopen($zn,'rb');
426         if(!$zip) return(null);
427         $cdir = $this->ReadCentralDir($zip,$zn);
428         $pos_entry = $cdir['offset'];
429
430         for ($i=0; $i<$cdir['entries']; $i++) {
431             @fseek($zip, $pos_entry);
432             $header = $this->ReadCentralFileHeaders($zip);
433             $header['index'] = $i;
434             $pos_entry = ftell($zip);
435             @rewind($zip);
436             fseek($zip, $header['offset']);
437             if ($name == $header['stored_filename'] || $name == $header['filename']) {
438                 $str = $this->ExtractStrFile($header, $zip);
439                 fclose($zip);
440                 return $str;
441             }
442
443         }
444         fclose($zip);
445         return null;
446     }
447
448     function ExtractStrFile($header,$zip) {
449         $hdr = $this->readfileheader($zip);
450         $binary_data = '';
451         if (!($header['external']==0x41FF0010) && !($header['external']==16)) {
452             if ($header['compression']==0) {
453                 while ($size != 0) {
454                     $read_size = ($size < 2048 ? $size : 2048);
455                     $buffer = fread($zip, $read_size);
456                     $binary_data .= pack('a'.$read_size, $buffer);
457                     $size -= $read_size;
458                 }
459                 return $binary_data;
460             } else {
461                 $size = $header['compressed_size'];
462                 if ($size == 0) {
463                     return '';
464                 }
465                 //Just in case
466                 if ($size > ($this->_ret_bytes(ini_get('memory_limit'))/2)) {
467                     die("Compressed file is to huge to be uncompress in memory.");
468                 }
469                 while ($size != 0)
470                 {
471                     $read_size = ($size < 2048 ? $size : 2048);
472                     $buffer = fread($zip, $read_size);
473                     $binary_data .= pack('a'.$read_size, $buffer);
474                     $size -= $read_size;
475                 }
476                 $str = gzinflate($binary_data, $header['size']);
477                 if ($header['crc'] == crc32($str)) {
478                     return $str;
479                 } else {
480                     die("Crc Error");
481                 }
482             }
483         }
484         return null;
485     }
486
487     function _ret_bytes($val) {
488         $val = trim($val);
489         $last = $val{strlen($val)-1};
490         switch($last) {
491             case 'k':
492             case 'K':
493                 return (int) $val * 1024;
494                 break;
495             case 'm':
496             case 'M':
497                 return (int) $val * 1048576;
498                 break;
499             default:
500                 return $val;
501         }
502     }
503 }
504