3 * TAR format class - Creates TAR archives
5 * This class is part or the MaxgComp suite and originally named
8 * Modified for Dokuwiki
11 * @link http://docs.maxg.info
12 * @author Bouchon <tarlib@bouchon.org> (Maxg)
13 * @author Christopher Smith <chris@jalakai.co.uk>
18 * Those constants represent the compression method to use.
19 * COMPRESS_GZIP is used for the GZIP compression; COMPRESS_BZIP for
20 * BZIP2 and COMPRESS_NONE for no compression.
22 * On the other hand, COMPRESS_AUTO is a bit harder. It will first check
23 * if the zlib extensions are loaded.
24 * If it is, GZIP will be used. Else it will check if the bz2 extensions
25 * are loaded. If it is, BZIP2 will be used. Else no compression will be
28 * You can then use getCompression() to know the compression chosen.
30 * If you selected a compression which can't be used (i.e extension not
31 * present), it will be just disabled, and won't produce any error !
32 * As a consequence, getCompression() will return COMPRESS_NONE
34 * ARCHIVE_DYNAMIC can be passed as the first argument of the constructor, to
35 * create an archive in memory instead of a file. See also: MaxgTar(),
36 * getDynamicArchive() and writeArchive()
38 * ARCHIVE_RENAMECOMP is a flag that can be multiplied by the compression method
39 * (i.e COMPRESS_AUTO * ARCHIVE_RENAMECOMP). This will add the correct extension
40 * to the archive name, which is useful with COMPRESS_AUTO, ie .bz2 if you gave
41 * COMPRESS_BZIP. See also getCompression(TRUE) which does exactly the
44 * COMPRESS_DETECT does exactly the opposite and try to detect the
45 * compression to use to read the archive depending on its extension. (i.e if
46 * the archive ends with .tar.gz TarLib will try to decompress it with
47 * GZIP). See also setCompression()
49 * FULL_ARCHIVE is a -1 constant that means "the complete archive" when
50 * extracting. This is explained in Extract()
52 define('COMPRESS_GZIP',1);
53 define('COMPRESS_BZIP',2);
54 define('COMPRESS_AUTO',3);
55 define('COMPRESS_NONE',0);
56 define('TARLIB_VERSION','1.2');
57 define('FULL_ARCHIVE',-1);
58 define('ARCHIVE_DYNAMIC',0);
59 define('ARCHIVE_RENAMECOMP',5);
60 define('COMPRESS_DETECT',-1);
73 * constructor, initialize the class
75 * The constructor initialize the variables and prepare the class for the
76 * archive, and return the object created. Note that you can use multiple
77 * instances of the MaxgTar class, if you call this function another time and
78 * store the object in an other variable.
80 * In fact, MaxgTar accepts the following arguments (all are optional) :
82 * filename can be either a file name (absolute or relative). In this
83 * case, it can be used both for reading and writing. You can also open
84 * remote archive if you add a protocole name at the beginning of the file
85 * (ie https://host.dom/archive.tar.gz), but for reading only and if the
86 * directive allow_url_fopen is enabled in PHP.INI (this can be checked with
87 * TarInfo()). If you pass a file that doesn't exist, the script
88 * will try to create it. If the archive already exists and contains files,
89 * you can use Add() to append files.But by default this parameter
90 * is ARCHIVE_DYNAMIC (write only) so the archive is created in memory and
91 * can be sent to a file [writeArchive()] or to the client
94 * compression_type should be a constant that represents a type of
95 * compression, or its integer value. The different values are described in
98 * compression_level is an integer between 1 and 9 (by default) an
99 * represent the GZIP or BZIP compression level. 1 produce fast compression,
100 * and 9 produce smaller files. See the RFC 1952 for more infos.
102 function tarlib($p_filen = ARCHIVE_DYNAMIC
, $p_comptype = COMPRESS_AUTO
, $p_complevel = 9)
104 $this->_initerror
= 0;
105 $this->_nomf
= $p_filen; $flag=0;
106 if($p_comptype && $p_comptype %
5 == 0){$p_comptype /= ARCHIVE_RENAMECOMP
; $flag=1;}
108 if($p_complevel > 0 && $p_complevel <= 9) $this->_compzlevel
= $p_complevel;
109 else $p_complevel = 9;
111 if($p_comptype == COMPRESS_DETECT
)
113 if(strtolower(substr($p_filen,-3)) == '.gz') $p_comptype = COMPRESS_GZIP
;
114 elseif(strtolower(substr($p_filen,-4)) == '.bz2') $p_comptype = COMPRESS_BZIP
;
115 else $p_comptype = COMPRESS_NONE
;
121 if(!extension_loaded('zlib')) $this->_initerror
= -1;
122 $this->_comptype
= COMPRESS_GZIP
;
126 if(!extension_loaded('bz2')) $this->_inierror
= -2;
127 $this->_comptype
= COMPRESS_BZIP
;
131 if(extension_loaded('zlib'))
132 $this->_comptype
= COMPRESS_GZIP
;
133 elseif(extension_loaded('bz2'))
134 $this->_comptype
= COMPRESS_BZIP
;
136 $this->_comptype
= COMPRESS_NONE
;
140 $this->_comptype
= COMPRESS_NONE
;
143 if($this->_init_error
< 0) $this->_comptype
= COMPRESS_NONE
;
145 if($flag) $this->_nomf
.= '.'.$this->getCompression(1);
146 $this->_result
= true;
150 * Recycle a TAR object.
152 * This function does exactly the same as TarLib (constructor), except it
153 * returns a status code.
155 function setArchive($p_name='', $p_comp = COMPRESS_AUTO
, $p_level=9)
158 $this->TarLib($p_name, $p_comp, $p_level);
159 return $this->_result
;
163 * Get the compression used to generate the archive
165 * This is a very useful function when you're using dynamical archives.
166 * Besides, if you let the script chose which compression to use, you'll have
167 * a problem when you'll want to send it to the client if you don't know
168 * which compression was used.
170 * There are two ways to call this function : if you call it without argument
171 * or with FALSE, it will return the compression constants, explained on the
172 * MaxgTar Constants. If you call it with GetExtension on TRUE it will
173 * return the extension without starting dot (ie "tar" or "tar.bz2" or
176 * NOTE: This can be done with the flag ARCHIVE_RENAMECOMP, see the
179 function getCompression($ext = false)
181 $exts = Array('tar','tar.gz','tar.bz2');
182 if($ext) return $exts[$this->_comptype
];
183 return $this->_comptype
;
187 * Change the compression mode.
189 * This function will change the compression methode to read or write
190 * the archive. See the MaxgTar Constants to see which constants you can use.
191 * It may look strange, but it returns the GZIP compression level.
193 function setCompression($p_comp = COMPRESS_AUTO
)
195 $this->setArchive($this->_nomf
, $p_comp, $this->_compzlevel
);
196 return $this->_compzlevel
;
200 * Returns the compressed dynamic archive.
202 * When you're working with dynamic archives, use this function to grab
203 * the final compressed archive in a string ready to be put in a SQL table or
206 function getDynamicArchive()
208 return $this->_encode($this->_memdat
);
212 * Write a dynamical archive into a file
214 * This function attempts to write a dynamicaly-genrated archive into
215 * TargetFile on the webserver. It returns a TarErrorStr() status
218 * To know the extension to add to the file if you're using AUTO_DETECT
219 * compression, you can use getCompression().
221 function writeArchive($p_archive) {
222 if(!$this->_memdat
) return -7;
223 $fp = @fopen
($p_archive, 'wb');
226 fwrite($fp, $this->_memdat
);
233 * Send a TAR archive to the client browser.
235 * This function will send an archive to the client, and return a status
236 * code, but can behave differently depending on the arguments you give. All
237 * arguments are optional.
239 * ClientName is used to specify the archive name to give to the browser. If
240 * you don't give one, it will send the constructor filename or return an
241 * error code in case of dynamical archive.
243 * FileName is optional and used to send a specific archive. Leave it blank
244 * to send dynamical archives or the current working archive.
246 * If SendHeaders is enabled (by default), the library will send the HTTP
247 * headers itself before it sends the contents. This headers are :
248 * Content-Type, Content-Disposition, Content-Length and Accept-Range.
250 * Please note that this function DOES NOT stops the script so don't forget
251 * to exit() to avoid your script sending other data and corrupt the archive.
252 * Another note : for AUTO_DETECT dynamical archives you can know the
253 * extension to add to the name with getCompression()
255 function sendClient($name = '', $archive = '', $headers = true)
257 if(!$name && !$this->_nomf
) return -9;
258 if(!$archive && !$this->_memdat
) return -10;
259 if(!$name) $name = basename($this->_nomf
);
261 if($archive){ if(!file_exists($archive)) return -11; }
262 else $decoded = $this->getDynamicArchive();
266 header('Content-Type: application/x-gtar');
267 header('Content-Disposition: attachment; filename='.basename($name));
268 header('Accept-Ranges: bytes');
269 header('Content-Length: '.($archive ?
filesize($archive) : strlen($decoded)));
274 $fp = @fopen
($archive,'rb');
277 while(!feof($fp)) echo fread($fp,2048);
288 * Extract part or totality of the archive.
290 * This function can extract files from an archive, and returns then a
291 * status codes that can be converted with TarErrorStr() into a
292 * human readable message.
294 * Only the first argument is required, What and it can be either the
295 * constant FULL_ARCHIVE or an indexed array containing each file you want to
298 * To contains the target folder to extract the archive. It is optional and
299 * the default value is '.' which means the current folder. If the target
300 * folder doesn't exist, the script attempts to create it and give it
301 * permissions 0777 by default.
303 * RemovePath is very usefull when you want to extract files from a subfoler
304 * in the archive to a root folder. For instance, if you have a file in the
305 * archive called some/sub/folder/test.txt and you want to extract it to the
306 * script folder, you can call Extract with To = '.' and RemovePath =
309 * FileMode is optional and its default value is 0755. It is in fact the UNIX
310 * permission in octal mode (prefixed with a 0) that will be given on each
313 function Extract($p_what = FULL_ARCHIVE
, $p_to = '.', $p_remdir='', $p_mode = 0755)
315 if(!$this->_OpenRead()) return -4;
316 // if(!@is_dir($p_to)) if(!@mkdir($p_to, 0777)) return -8; --CS
317 if(!@is_dir
($p_to)) if(!$this->_dirApp($p_to)) return -8; //--CS (route through correct dir fn)
319 $ok = $this->_extractList($p_to, $p_what, $p_remdir, $p_mode);
326 * Create a new package with the given files
328 * This function will attempt to create a new archive with global headers
329 * then add the given files into. If the archive is a real file, the
330 * contents are written directly into the file, if it is a dynamic archive
331 * contents are only stored in memory. This function should not be used to
332 * add files to an existing archive, you should use Add() instead.
334 * The FileList actually supports three different modes :
336 * - You can pass a string containing filenames separated by pipes '|'.
337 * In this case the file are read from the webserver filesystem and the
338 * root folder is the folder where the script using the MaxgTar is called.
340 * - You can also give a unidimensional indexed array containing the
341 * filenames. The behaviour for the content reading is the same that a
344 * - The more useful usage is to pass bidimensional arrays, where the
345 * first element contains the filename and the second contains the file
346 * contents. You can even add empty folders to the package if the filename
347 * has a leading '/'. Once again, have a look at the exemples to understand
350 * Note you can also give arrays with both dynamic contents and static files.
352 * The optional parameter RemovePath can be used to delete a part of the tree
353 * of the filename you're adding, for instance if you're adding in the root
354 * of a package a file that is stored somewhere in the server tree.
356 * On the contrary the parameter AddPath can be used to add a prefix folder
357 * to the file you store. Note also that the RemovePath is applied before the
358 * AddPath is added, so it HAS a sense to use both parameters together.
360 function Create($p_filelist,$p_add='',$p_rem='')
362 if(!$fl = $this->_fetchFilelist($p_filelist)) return -5;
363 if(!$this->_OpenWrite()) return -6;
365 $ok = $this->_addFileList($fl,$p_add,$p_rem);
367 if($ok) $this->_writeFooter();
368 else{ $this->_CompTar(); @unlink
($this->_nomf
); }
374 * Add files to an existing package.
376 * This function does exactly the same than Create() exept it
377 * will append the given files at the end of the archive. Please not the is
378 * safe to call Add() on a newly created archive whereas the
379 * contrary will fail !
381 * This function returns a status code, you can use TarErrorStr() on
382 * it to get the human-readable description of the error.
384 function Add($p_filelist, $p_add = '', $p_rem = '') { if (($this->_nomf
385 != ARCHIVE_DYNAMIC
&& @is_file
($this->_nomf
)) ||
($this->_nomf
==
386 ARCHIVE_DYNAMIC
&& !$this->_memdat
)) return $this->Create($p_filelist,
389 if(!$fl = $this->_fetchFilelist($p_filelist)) return -5;
390 return $this->_append($fl, $p_add, $p_rem);
394 * Read the contents of a TAR archive
396 * This function attempts to get the list of the files stored in the
397 * archive, and return either an error code or an indexed array of
398 * associative array containing for each file the following informations :
400 * checksum Tar Checksum of the file
401 * filename The full name of the stored file (up to 100 c.)
402 * mode UNIX permissions in DECIMAL, not octal
405 * size Uncompressed filesize
406 * mtime Timestamp of last modification
407 * typeflag Empty for files, set for folders
408 * link For the links, did you guess it ?
412 function ListContents()
414 if(!$this->_nomf
) return -3;
415 if(!$this->_OpenRead()) return -4;
419 while ($dat = $this->_read(512))
421 $dat = $this->_readHeader($dat);
422 if(!is_array($dat)) continue;
424 $this->_seek(ceil($dat['size']/512)*512,1);
432 * Convert a status code into a human readable message
434 * Some MaxgTar functions like Create(), Add() ... return numerical
435 * status code. You can pass them to this function to grab their english
438 function TarErrorStr($i)
442 0 => "Undocumented error",
443 -1 => "Can't use COMPRESS_GZIP compression : ZLIB extensions are not loaded !",
444 -2 => "Can't use COMPRESS_BZIP compression : BZ2 extensions are not loaded !",
445 -3 => "You must set a archive file to read the contents !",
446 -4 => "Can't open the archive file for read !",
447 -5 => "Invalide file list !",
448 -6 => "Can't open the archive in write mode !",
449 -7 => "There is no ARCHIVE_DYNAMIC to write !",
450 -8 => "Can't create the directory to extract files !",
451 -9 => "Please pass a archive name to send if you made created an ARCHIVE_DYNAMIC !",
452 -10 => "You didn't pass an archive filename and there is no stored ARCHIVE_DYNAMIC !",
453 -11 => "Given archive doesn't exist !"
456 return isset($ecodes[$i]) ?
$ecodes[$i] : $ecodes[0];
460 * Display informations about the MaxgTar Class.
462 * This function will display vaious informations about the server
463 * MaxgTar is running on.
465 * The optional parameter DispHeaders is used to generate a full page with
466 * HTML headers (TRUE by default) or just the table with the informations
467 * (FALSE). Note that the HTML page generated is verified compatible XHTML
468 * 1.0, but not HTML 4.0 compatible.
470 function TarInfo($headers = true)
475 <!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
476 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
480 <title
>MaxgComp TAR
</title
>
481 <style type
="text/css">
483 body
,td
{font
-size
:10pt
;font
-family
: arial
;}
485 <meta name
="Author" content
="The Maxg Network, http://maxg.info" />
486 <meta http
-equiv
="Content-Type" content
="text/html; charset=iso-8859-1" />
489 <body bgcolor
="#EFEFEF">
493 <table border
="0" align
="center" width
="500" cellspacing
="4" cellpadding
="5" style
="border:1px dotted black;">
495 <td align
="center" bgcolor
="#DFDFEF" colspan
="3" style
="font-size:15pt;font-color:#330000;border:1px solid black;">MaxgComp TAR
</td
>
498 <td colspan
="2" bgcolor
="#EFEFFE" style
="border:1px solid black;">This software was created by the Maxg Network
, <a href
="http://maxg.info" target
="_blank" style
="text-decoration:none;color:#333366;">http
://maxg.info</a>
499 <br
/>It is distributed under the GNU
<a href
="http://www.gnu.org/copyleft/lesser.html" target
="_blank" style
="text-decoration:none;color:#333366;">Lesser General
Public License
</a
>
500 <br
/>You can find the documentation of this
class <a href
="http://docs.maxg.info" target
="_blank" style
="text-decoration:none;color:#333366;">here
</a
></td
>
501 <td width
="60" bgcolor
="#EFEFFE" style
="border:1px solid black;" align
="center"><img src
="http://img.maxg.info/menu/tar.gif" border
="0" alt
="MaxgComp TAR" /></td
>
504 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">MaxgComp TAR version
</td
>
505 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=TARLIB_VERSION?
></td
>
508 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">ZLIB extensions
</td
>
509 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=(extension_loaded('zlib') ?
'<b>Yes</b>' : '<i>No</i>')?
></td
>
512 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">BZ2 extensions
</td
>
513 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=(extension_loaded('bz2') ?
'<b>Yes</b>' : '<i>No</i>')?
></td
>
516 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">Allow URL fopen
</td
>
517 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=(ini_get('allow_url_fopen') ?
'<b>Yes</b>' : '<i>No</i>')?
></td
>
520 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">Time limit
</td
>
521 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=ini_get('max_execution_time')?
></td
>
524 <td width
="50%" align
="center" style
="border:1px solid black;" bgcolor
="#DFDFEF">PHP Version
</td
>
525 <td colspan
="2" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;"><?
=phpversion()?
></td
>
528 <td colspan
="3" align
="center" bgcolor
="#EFEFFE" style
="border:1px solid black;">
529 <i
>Special thanks to
«
; Vincent Blavet
»
; for his PEAR
::Archive_Tar
class</i
>
534 if($headers) echo '</body></html>';
537 function _seek($p_flen, $tell=0)
539 if($this->_nomf
=== ARCHIVE_DYNAMIC
)
540 $this->_memdat
=substr($this->_memdat
,0,($tell ?
strlen($this->_memdat
) : 0) +
$p_flen);
541 elseif($this->_comptype
== COMPRESS_GZIP
)
542 @gzseek
($this->_fp
, ($tell ? @gztell
($this->_fp
) : 0)+
$p_flen);
543 elseif($this->_comptype
== COMPRESS_BZIP
)
544 @fseek
($this->_fp
, ($tell ? @ftell
($this->_fp
) : 0)+
$p_flen);
546 @fseek
($this->_fp
, ($tell ? @ftell
($this->_fp
) : 0)+
$p_flen);
551 if($this->_comptype
== COMPRESS_GZIP
)
552 $this->_fp
= @gzopen
($this->_nomf
, 'rb');
553 elseif($this->_comptype
== COMPRESS_BZIP
)
554 $this->_fp
= @bzopen
($this->_nomf
, 'rb');
556 $this->_fp
= @fopen
($this->_nomf
, 'rb');
558 return ($this->_fp ?
true : false);
561 function _OpenWrite($add = 'w')
563 if($this->_nomf
=== ARCHIVE_DYNAMIC
) return true;
565 if($this->_comptype
== COMPRESS_GZIP
)
566 $this->_fp
= @gzopen
($this->_nomf
, $add.'b'.$this->_compzlevel
);
567 elseif($this->_comptype
== COMPRESS_BZIP
)
568 $this->_fp
= @bzopen
($this->_nomf
, $add.'b');
570 $this->_fp
= @fopen
($this->_nomf
, $add.'b');
572 return ($this->_fp ?
true : false);
577 if($this->_nomf
=== ARCHIVE_DYNAMIC ||
!$this->_fp
) return;
579 if($this->_comptype
== COMPRESS_GZIP
) @gzclose
($this->_fp
);
580 elseif($this->_comptype
== COMPRESS_BZIP
) @bzclose
($this->_fp
);
581 else @fclose
($this->_fp
);
584 function _read($p_len)
586 if($this->_comptype
== COMPRESS_GZIP
)
587 return @gzread
($this->_fp
,$p_len);
588 elseif($this->_comptype
== COMPRESS_BZIP
)
589 return @bzread
($this->_fp
,$p_len);
591 return @fread
($this->_fp
,$p_len);
594 function _write($p_data)
596 if($this->_nomf
=== ARCHIVE_DYNAMIC
) $this->_memdat
.= $p_data;
597 elseif($this->_comptype
== COMPRESS_GZIP
)
598 return @gzwrite
($this->_fp
,$p_data);
600 elseif($this->_comptype
== COMPRESS_BZIP
)
601 return @bzwrite
($this->_fp
,$p_data);
604 return @fwrite
($this->_fp
,$p_data);
607 function _encode($p_dat)
609 if($this->_comptype
== COMPRESS_GZIP
)
610 return gzencode($p_dat, $this->_compzlevel
);
611 elseif($this->_comptype
== COMPRESS_BZIP
)
612 return bzcompress($p_dat, $this->_compzlevel
);
616 function _readHeader($p_dat)
618 if (!$p_dat ||
strlen($p_dat) != 512) return false;
620 for ($i=0, $chks=0; $i<148; $i++
)
621 $chks +
= ord($p_dat[$i]);
623 for ($i=156,$chks+
=256; $i<512; $i++
)
624 $chks +
= ord($p_dat[$i]);
626 $headers = @unpack
("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $p_dat);
627 if(!$headers) return false;
629 $return['checksum'] = OctDec(trim($headers['checksum']));
630 if ($return['checksum'] != $chks) return false;
632 $return['filename'] = trim($headers['filename']);
633 $return['mode'] = OctDec(trim($headers['mode']));
634 $return['uid'] = OctDec(trim($headers['uid']));
635 $return['gid'] = OctDec(trim($headers['gid']));
636 $return['size'] = OctDec(trim($headers['size']));
637 $return['mtime'] = OctDec(trim($headers['mtime']));
638 $return['typeflag'] = $headers['typeflag'];
639 $return['link'] = trim($headers['link']);
640 $return['uname'] = trim($headers['uname']);
641 $return['gname'] = trim($headers['gname']);
646 function _fetchFilelist($p_filelist)
648 if(!$p_filelist ||
(is_array($p_filelist) && !@count
($p_filelist))) return false;
650 if(is_string($p_filelist))
652 $p_filelist = explode('|',$p_filelist);
653 if(!is_array($p_filelist)) $p_filelist = Array($p_filelist);
659 function _addFileList($p_fl, $p_addir, $p_remdir)
661 foreach($p_fl as $file)
663 if(($file == $this->_nomf
&& $this->_nomf
!= ARCHIVE_DYNAMIC
) ||
!$file ||
(!file_exists($file) && !is_array($file)))
666 if (!$this->_addFile($file, $p_addir, $p_remdir))
671 $d = @opendir
($file);
674 readdir($d); readdir($d);
676 while($f = readdir($d))
678 if($file != ".") $tmplist[0] = "$file/$f";
679 else $tmplist[0] = $d;
681 $this->_addFileList($tmplist, $p_addir, $p_remdir);
684 closedir($d); unset($tmplist,$f);
690 function _addFile($p_fn, $p_addir = '', $p_remdir = '')
692 if(is_array($p_fn)) list($p_fn, $data) = $p_fn;
697 if(substr($p_remdir,-1) != '/') $p_remdir .= "/";
699 if(substr($sname, 0, strlen($p_remdir)) == $p_remdir)
700 $sname = substr($sname, strlen($p_remdir));
703 if($p_addir) $sname = $p_addir.(substr($p_addir,-1) == '/' ?
'' : "/").$sname;
705 if(strlen($sname) > 99) return;
709 if(!$this->_writeFileHeader($p_fn, $sname)) return false;
715 $fp = fopen($p_fn, 'rb');
716 if(!$fp) return false;
719 if(!$this->_writeFileHeader($p_fn, $sname, ($data ?
strlen($data) : false))) return false;
725 $packed = pack("a512", fread($fp,512));
726 $this->_write($packed);
732 for($s = 0; $s < strlen($data); $s +
= 512)
733 $this->_write(pack("a512",substr($data,$s,512)));
740 function _writeFileHeader($p_file, $p_sname, $p_data=false)
744 if (!$p_sname) $p_sname = $p_file;
745 $p_sname = $this->_pathTrans($p_sname);
747 $h_info = stat($p_file);
748 $h[0] = sprintf("%6s ", DecOct($h_info[4]));
749 $h[] = sprintf("%6s ", DecOct($h_info[5]));
750 $h[] = sprintf("%6s ", DecOct(fileperms($p_file)));
752 $h[] = sprintf("%11s ", DecOct(filesize($p_file)));
753 $h[] = sprintf("%11s", DecOct(filemtime($p_file)));
755 $dir = @is_dir
($p_file) ?
'5' : '';
760 $p_data = sprintf("%11s ", DecOct($p_data));
761 $time = sprintf("%11s ", DecOct(time()));
762 $h = Array(" 0 "," 0 "," 40777 ",$p_data,$time);
765 $data_first = pack("a100a8a8a8a12A12", $p_sname, $h[2], $h[0], $h[1], $h[3], $h[4]);
766 $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', "");
768 for ($i=0,$chks=0; $i<148; $i++
)
769 $chks +
= ord($data_first[$i]);
771 for ($i=156, $chks+
=256, $j=0; $i<512; $i++
, $j++
)
772 $chks +
= ord($data_last[$j]);
774 $this->_write($data_first);
776 $chks = pack("a8",sprintf("%6s ", DecOct($chks)));
777 $this->_write($chks.$data_last);
782 function _append($p_filelist, $p_addir="", $p_remdir="")
784 if(!$this->_fp
) if(!$this->_OpenWrite('a')) return -6;
786 if($this->_nomf
== ARCHIVE_DYNAMIC
)
788 $s = strlen($this->_memdat
);
789 $this->_memdat
= substr($this->_memdat
,0,-512);
793 $s = filesize($this->_nomf
);
794 $this->_seek($s-512);
797 $ok = $this->_addFileList($p_filelist, $p_addir, $p_remdir);
798 $this->_writeFooter();
803 function _pathTrans($p_dir)
807 $subf = explode("/", $p_dir); $r='';
809 for ($i=count($subf)-1; $i>=0; $i--)
811 if ($subf[$i] == ".") {}
812 else if ($subf[$i] == "..") $i--;
813 else if (!$subf[$i] && $i!=count($subf)-1 && $i) {}
814 else $r = $subf[$i].($i!=(count($subf)-1) ?
"/".$r : "");
820 function _writeFooter()
822 $this->_write(pack("a512", ""));
825 function _extractList($p_to, $p_files, $p_remdir, $p_mode = 0755)
827 if (!$p_to ||
($p_to[0]!="/"&&substr($p_to,0,3)!="../"&&substr($p_to,1,3)!=":\\"&&substr($p_to,1,2)!=":/")) /*" // <- PHP Coder bug */
830 if ($p_remdir && substr($p_remdir,-1)!='/') $p_remdir .= '/';
831 $p_remdirs = strlen($p_remdir);
832 while($dat = $this->_read(512))
834 $headers = $this->_readHeader($dat);
835 if(!$headers['filename']) continue;
837 if($p_files == -1 ||
$p_files[0] == -1) $extract = true;
842 foreach($p_files as $f)
844 if(substr($f,-1) == "/") {
845 if((strlen($headers['filename']) > strlen($f)) && (substr($headers['filename'],0,strlen($f))==$f)) {
846 $extract = true; break;
849 elseif($f == $headers['filename']) {
850 $extract = true; break;
858 if ($p_remdir && substr($headers['filename'],0,$p_remdirs)==$p_remdir)
859 $headers['filename'] = substr($headers['filename'],$p_remdirs);
861 if($headers['filename'].'/' == $p_remdir && $headers['typeflag']=='5') continue;
863 if ($p_to != "./" && $p_to != "/")
865 while($p_to{-1}=="/") $p_to = substr($p_to,0,-1);
867 if($headers['filename']{0} == "/")
868 $headers['filename'] = $p_to.$headers['filename'];
870 $headers['filename'] = $p_to."/".$headers['filename'];
873 $ok = $this->_dirApp($headers['typeflag']=="5" ?
$headers['filename'] : dirname($headers['filename']));
874 if($ok < 0) return $ok;
876 if (!$headers['typeflag'])
878 if (!$fp = @fopen
($headers['filename'], "wb")) return -6;
879 $n = floor($headers['size']/512);
881 for ($i=0; $i<$n; $i++
) fwrite($fp, $this->_read(512),512);
882 if (($headers['size'] %
512) != 0) fwrite($fp, $this->_read(512), $headers['size'] %
512);
885 touch($headers['filename'], $headers['mtime']);
886 chmod($headers['filename'], $p_mode);
890 $this->_seek(ceil($headers['size']/512)*512,1);
892 }else $this->_seek(ceil($headers['size']/512)*512,1);
899 // map to dokuwiki function (its more robust)
900 return io_mkdir_p($d);
902 $d = explode('/', $d);
907 if(!is_dir($base.$f))
909 $ok = @mkdir($base.$f, 0777);
910 if(!$ok) return false;