1 | <?php
|
---|
2 | /**
|
---|
3 | * TAR format class - Creates TAR archives
|
---|
4 | *
|
---|
5 | * This class is part or the MaxgComp suite and originally named
|
---|
6 | * MaxgTar class.
|
---|
7 | *
|
---|
8 | * Modified for Dokuwiki
|
---|
9 | *
|
---|
10 | * @license LGPL-2.1
|
---|
11 | * @link http://docs.maxg.info
|
---|
12 | * @author Bouchon <[email protected]> (Maxg)
|
---|
13 | * @author Christopher Smith <[email protected]>
|
---|
14 | */
|
---|
15 |
|
---|
16 |
|
---|
17 | /**
|
---|
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.
|
---|
21 | *
|
---|
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
|
---|
26 | * performed.
|
---|
27 | *
|
---|
28 | * You can then use getCompression() to know the compression chosen.
|
---|
29 | *
|
---|
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
|
---|
33 | *
|
---|
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()
|
---|
37 | *
|
---|
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
|
---|
42 | * same
|
---|
43 | *
|
---|
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()
|
---|
48 | *
|
---|
49 | * FULL_ARCHIVE is a -1 constant that means "the complete archive" when
|
---|
50 | * extracting. This is explained in Extract()
|
---|
51 | */
|
---|
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);
|
---|
61 |
|
---|
62 | class TarLib {
|
---|
63 | var $_comptype;
|
---|
64 | var $_compzlevel;
|
---|
65 | var $_fp;
|
---|
66 | var $_memdat;
|
---|
67 | var $_nomf;
|
---|
68 | var $_result;
|
---|
69 | var $_initerror;
|
---|
70 |
|
---|
71 | const COMPRESS_GZIP = 1;
|
---|
72 | const COMPRESS_BZIP = 2;
|
---|
73 | const COMPRESS_AUTO = 3;
|
---|
74 | const COMPRESS_NONE = 0;
|
---|
75 | const TARLIB_VERSION = '1.2';
|
---|
76 | const FULL_ARCHIVE = -1;
|
---|
77 | const ARCHIVE_DYNAMIC = 0;
|
---|
78 | const ARCHIVE_RENAMECOMP = 5;
|
---|
79 | const COMPRESS_DETECT = -1;
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * constructor, initialize the class
|
---|
83 | *
|
---|
84 | * The constructor initialize the variables and prepare the class for the
|
---|
85 | * archive, and return the object created. Note that you can use multiple
|
---|
86 | * instances of the MaxgTar class, if you call this function another time and
|
---|
87 | * store the object in an other variable.
|
---|
88 | *
|
---|
89 | * In fact, MaxgTar accepts the following arguments (all are optional) :
|
---|
90 | *
|
---|
91 | * filename can be either a file name (absolute or relative). In this
|
---|
92 | * case, it can be used both for reading and writing. You can also open
|
---|
93 | * remote archive if you add a protocole name at the beginning of the file
|
---|
94 | * (ie https://host.dom/archive.tar.gz), but for reading only and if the
|
---|
95 | * directive allow_url_fopen is enabled in PHP.INI (this can be checked with
|
---|
96 | * TarInfo()). If you pass a file that doesn't exist, the script
|
---|
97 | * will try to create it. If the archive already exists and contains files,
|
---|
98 | * you can use Add() to append files.But by default this parameter
|
---|
99 | * is ARCHIVE_DYNAMIC (write only) so the archive is created in memory and
|
---|
100 | * can be sent to a file [writeArchive()] or to the client
|
---|
101 | * [sendClient()]
|
---|
102 | *
|
---|
103 | * compression_type should be a constant that represents a type of
|
---|
104 | * compression, or its integer value. The different values are described in
|
---|
105 | * the constants.
|
---|
106 | *
|
---|
107 | * compression_level is an integer between 1 and 9 (by default) an
|
---|
108 | * represent the GZIP or BZIP compression level. 1 produce fast compression,
|
---|
109 | * and 9 produce smaller files. See the RFC 1952 for more infos.
|
---|
110 | */
|
---|
111 | function tarlib($p_filen = TarLib::ARCHIVE_DYNAMIC , $p_comptype = TarLib::COMPRESS_AUTO, $p_complevel = 9) {
|
---|
112 | $this->_initerror = 0;
|
---|
113 | $this->_nomf = $p_filen;
|
---|
114 | $flag=0;
|
---|
115 | if($p_comptype && $p_comptype % 5 == 0){
|
---|
116 | $p_comptype /= TarLib::ARCHIVE_RENAMECOMP;
|
---|
117 | $flag=1;
|
---|
118 | }
|
---|
119 |
|
---|
120 | if($p_complevel > 0 && $p_complevel <= 9) $this->_compzlevel = $p_complevel;
|
---|
121 | else $p_complevel = 9;
|
---|
122 |
|
---|
123 | if($p_comptype == TarLib::COMPRESS_DETECT) {
|
---|
124 | if(strtolower(substr($p_filen,-3)) == '.gz') $p_comptype = TarLib::COMPRESS_GZIP;
|
---|
125 | elseif(strtolower(substr($p_filen,-4)) == '.bz2') $p_comptype = TarLib::COMPRESS_BZIP;
|
---|
126 | else $p_comptype = TarLib::COMPRESS_NONE;
|
---|
127 | }
|
---|
128 |
|
---|
129 | switch($p_comptype) {
|
---|
130 | case TarLib::COMPRESS_GZIP:
|
---|
131 | if(!extension_loaded('zlib')) $this->_initerror = -1;
|
---|
132 | $this->_comptype = TarLib::COMPRESS_GZIP;
|
---|
133 | break;
|
---|
134 |
|
---|
135 | case TarLib::COMPRESS_BZIP:
|
---|
136 | if(!extension_loaded('bz2')) $this->_initerror = -2;
|
---|
137 | $this->_comptype = TarLib::COMPRESS_BZIP;
|
---|
138 | break;
|
---|
139 |
|
---|
140 | case TarLib::COMPRESS_AUTO:
|
---|
141 | if(extension_loaded('zlib'))
|
---|
142 | $this->_comptype = TarLib::COMPRESS_GZIP;
|
---|
143 | elseif(extension_loaded('bz2'))
|
---|
144 | $this->_comptype = TarLib::COMPRESS_BZIP;
|
---|
145 | else
|
---|
146 | $this->_comptype = TarLib::COMPRESS_NONE;
|
---|
147 | break;
|
---|
148 |
|
---|
149 | default:
|
---|
150 | $this->_comptype = TarLib::COMPRESS_NONE;
|
---|
151 | }
|
---|
152 |
|
---|
153 | if($this->_initerror < 0) $this->_comptype = TarLib::COMPRESS_NONE;
|
---|
154 |
|
---|
155 | if($flag) $this->_nomf.= '.'.$this->getCompression(1);
|
---|
156 | $this->_result = true;
|
---|
157 | }
|
---|
158 |
|
---|
159 | /**
|
---|
160 | * Recycle a TAR object.
|
---|
161 | *
|
---|
162 | * This function does exactly the same as TarLib (constructor), except it
|
---|
163 | * returns a status code.
|
---|
164 | */
|
---|
165 | function setArchive($p_name='', $p_comp = TarLib::COMPRESS_AUTO, $p_level=9) {
|
---|
166 | $this->_CompTar();
|
---|
167 | $this->TarLib($p_name, $p_comp, $p_level);
|
---|
168 | return $this->_result;
|
---|
169 | }
|
---|
170 |
|
---|
171 | /**
|
---|
172 | * Get the compression used to generate the archive
|
---|
173 | *
|
---|
174 | * This is a very useful function when you're using dynamical archives.
|
---|
175 | * Besides, if you let the script chose which compression to use, you'll have
|
---|
176 | * a problem when you'll want to send it to the client if you don't know
|
---|
177 | * which compression was used.
|
---|
178 | *
|
---|
179 | * There are two ways to call this function : if you call it without argument
|
---|
180 | * or with FALSE, it will return the compression constants, explained on the
|
---|
181 | * MaxgTar Constants. If you call it with GetExtension on TRUE it will
|
---|
182 | * return the extension without starting dot (ie "tar" or "tar.bz2" or
|
---|
183 | * "tar.gz")
|
---|
184 | *
|
---|
185 | * NOTE: This can be done with the flag ARCHIVE_RENAMECOMP, see the
|
---|
186 | * MaxgTar Constants
|
---|
187 | */
|
---|
188 | function getCompression($ext = false) {
|
---|
189 | $exts = Array('tar','tar.gz','tar.bz2');
|
---|
190 | if($ext) return $exts[$this->_comptype];
|
---|
191 | return $this->_comptype;
|
---|
192 | }
|
---|
193 |
|
---|
194 | /**
|
---|
195 | * Change the compression mode.
|
---|
196 | *
|
---|
197 | * This function will change the compression methode to read or write
|
---|
198 | * the archive. See the MaxgTar Constants to see which constants you can use.
|
---|
199 | * It may look strange, but it returns the GZIP compression level.
|
---|
200 | */
|
---|
201 | function setCompression($p_comp = TarLib::COMPRESS_AUTO) {
|
---|
202 | $this->setArchive($this->_nomf, $p_comp, $this->_compzlevel);
|
---|
203 | return $this->_compzlevel;
|
---|
204 | }
|
---|
205 |
|
---|
206 | /**
|
---|
207 | * Returns the compressed dynamic archive.
|
---|
208 | *
|
---|
209 | * When you're working with dynamic archives, use this function to grab
|
---|
210 | * the final compressed archive in a string ready to be put in a SQL table or
|
---|
211 | * in a file.
|
---|
212 | */
|
---|
213 | function getDynamicArchive() {
|
---|
214 | return $this->_encode($this->_memdat);
|
---|
215 | }
|
---|
216 |
|
---|
217 | /**
|
---|
218 | * Write a dynamical archive into a file
|
---|
219 | *
|
---|
220 | * This function attempts to write a dynamicaly-genrated archive into
|
---|
221 | * TargetFile on the webserver. It returns a TarErrorStr() status
|
---|
222 | * code.
|
---|
223 | *
|
---|
224 | * To know the extension to add to the file if you're using AUTO_DETECT
|
---|
225 | * compression, you can use getCompression().
|
---|
226 | */
|
---|
227 | function writeArchive($p_archive) {
|
---|
228 | if(!$this->_memdat) return -7;
|
---|
229 | $fp = @fopen($p_archive, 'wb');
|
---|
230 | if(!$fp) return -6;
|
---|
231 |
|
---|
232 | fwrite($fp, $this->_memdat);
|
---|
233 | fclose($fp);
|
---|
234 |
|
---|
235 | return true;
|
---|
236 | }
|
---|
237 |
|
---|
238 | /**
|
---|
239 | * Send a TAR archive to the client browser.
|
---|
240 | *
|
---|
241 | * This function will send an archive to the client, and return a status
|
---|
242 | * code, but can behave differently depending on the arguments you give. All
|
---|
243 | * arguments are optional.
|
---|
244 | *
|
---|
245 | * ClientName is used to specify the archive name to give to the browser. If
|
---|
246 | * you don't give one, it will send the constructor filename or return an
|
---|
247 | * error code in case of dynamical archive.
|
---|
248 | *
|
---|
249 | * FileName is optional and used to send a specific archive. Leave it blank
|
---|
250 | * to send dynamical archives or the current working archive.
|
---|
251 | *
|
---|
252 | * If SendHeaders is enabled (by default), the library will send the HTTP
|
---|
253 | * headers itself before it sends the contents. This headers are :
|
---|
254 | * Content-Type, Content-Disposition, Content-Length and Accept-Range.
|
---|
255 | *
|
---|
256 | * Please note that this function DOES NOT stops the script so don't forget
|
---|
257 | * to exit() to avoid your script sending other data and corrupt the archive.
|
---|
258 | * Another note : for AUTO_DETECT dynamical archives you can know the
|
---|
259 | * extension to add to the name with getCompression()
|
---|
260 | */
|
---|
261 | function sendClient($name = '', $archive = '', $headers = true) {
|
---|
262 | if(!$name && !$this->_nomf) return -9;
|
---|
263 | if(!$archive && !$this->_memdat) return -10;
|
---|
264 | if(!$name) $name = basename($this->_nomf);
|
---|
265 |
|
---|
266 | if($archive){ if(!file_exists($archive)) return -11; }
|
---|
267 | else $decoded = $this->getDynamicArchive();
|
---|
268 |
|
---|
269 | if($headers) {
|
---|
270 | header('Content-Type: application/x-gtar');
|
---|
271 | header('Content-Disposition: attachment; filename='.basename($name));
|
---|
272 | header('Accept-Ranges: bytes');
|
---|
273 | header('Content-Length: '.($archive ? filesize($archive) : strlen($decoded)));
|
---|
274 | }
|
---|
275 |
|
---|
276 | if($archive) {
|
---|
277 | $fp = @fopen($archive,'rb');
|
---|
278 | if(!$fp) return -4;
|
---|
279 |
|
---|
280 | while(!feof($fp)) echo fread($fp,2048);
|
---|
281 | } else {
|
---|
282 | echo $decoded;
|
---|
283 | }
|
---|
284 |
|
---|
285 | return true;
|
---|
286 | }
|
---|
287 |
|
---|
288 | /**
|
---|
289 | * Extract part or totality of the archive.
|
---|
290 | *
|
---|
291 | * This function can extract files from an archive, and returns then a
|
---|
292 | * status codes that can be converted with TarErrorStr() into a
|
---|
293 | * human readable message.
|
---|
294 | *
|
---|
295 | * Only the first argument is required, What and it can be either the
|
---|
296 | * constant FULL_ARCHIVE or an indexed array containing each file you want to
|
---|
297 | * extract.
|
---|
298 | *
|
---|
299 | * To contains the target folder to extract the archive. It is optional and
|
---|
300 | * the default value is '.' which means the current folder. If the target
|
---|
301 | * folder doesn't exist, the script attempts to create it and give it
|
---|
302 | * permissions 0777 by default.
|
---|
303 | *
|
---|
304 | * RemovePath is very usefull when you want to extract files from a subfoler
|
---|
305 | * in the archive to a root folder. For instance, if you have a file in the
|
---|
306 | * archive called some/sub/folder/test.txt and you want to extract it to the
|
---|
307 | * script folder, you can call Extract with To = '.' and RemovePath =
|
---|
308 | * 'some/sub/folder/'
|
---|
309 | *
|
---|
310 | * FileMode is optional and its default value is 0755. It is in fact the UNIX
|
---|
311 | * permission in octal mode (prefixed with a 0) that will be given on each
|
---|
312 | * extracted file.
|
---|
313 | */
|
---|
314 | function Extract($p_what = TarLib::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)
|
---|
318 |
|
---|
319 | $ok = $this->_extractList($p_to, $p_what, $p_remdir, $p_mode);
|
---|
320 | $this->_CompTar();
|
---|
321 |
|
---|
322 | return $ok;
|
---|
323 | }
|
---|
324 |
|
---|
325 | /**
|
---|
326 | * Create a new package with the given files
|
---|
327 | *
|
---|
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.
|
---|
333 | *
|
---|
334 | * The FileList actually supports three different modes :
|
---|
335 | *
|
---|
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.
|
---|
339 | *
|
---|
340 | * - You can also give a unidimensional indexed array containing the
|
---|
341 | * filenames. The behaviour for the content reading is the same that a
|
---|
342 | * '|'ed string.
|
---|
343 | *
|
---|
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
|
---|
348 | * better.
|
---|
349 | *
|
---|
350 | * Note you can also give arrays with both dynamic contents and static files.
|
---|
351 | *
|
---|
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.
|
---|
355 | *
|
---|
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.
|
---|
359 | */
|
---|
360 | function Create($p_filelist,$p_add='',$p_rem='') {
|
---|
361 | if(!$fl = $this->_fetchFilelist($p_filelist)) return -5;
|
---|
362 | if(!$this->_OpenWrite()) return -6;
|
---|
363 |
|
---|
364 | $ok = $this->_addFileList($fl,$p_add,$p_rem);
|
---|
365 |
|
---|
366 | if($ok){
|
---|
367 | $this->_writeFooter();
|
---|
368 | }else{
|
---|
369 | $this->_CompTar();
|
---|
370 | @unlink($this->_nomf);
|
---|
371 | }
|
---|
372 |
|
---|
373 | return $ok;
|
---|
374 | }
|
---|
375 |
|
---|
376 | /**
|
---|
377 | * Add files to an existing package.
|
---|
378 | *
|
---|
379 | * This function does exactly the same than Create() exept it
|
---|
380 | * will append the given files at the end of the archive. Please not the is
|
---|
381 | * safe to call Add() on a newly created archive whereas the
|
---|
382 | * contrary will fail !
|
---|
383 | *
|
---|
384 | * This function returns a status code, you can use TarErrorStr() on
|
---|
385 | * it to get the human-readable description of the error.
|
---|
386 | */
|
---|
387 | function Add($p_filelist, $p_add = '', $p_rem = '') {
|
---|
388 | if (($this->_nomf != TarLib::ARCHIVE_DYNAMIC && @is_file($this->_nomf)) ||
|
---|
389 | ($this->_nomf == TarLib::ARCHIVE_DYNAMIC && !$this->_memdat)){
|
---|
390 | return $this->Create($p_filelist, $p_add, $p_rem);
|
---|
391 | }
|
---|
392 |
|
---|
393 | if(!$fl = $this->_fetchFilelist($p_filelist)) return -5;
|
---|
394 | return $this->_append($fl, $p_add, $p_rem);
|
---|
395 | }
|
---|
396 |
|
---|
397 | /**
|
---|
398 | * Read the contents of a TAR archive
|
---|
399 | *
|
---|
400 | * This function attempts to get the list of the files stored in the
|
---|
401 | * archive, and return either an error code or an indexed array of
|
---|
402 | * associative array containing for each file the following information :
|
---|
403 | *
|
---|
404 | * checksum Tar Checksum of the file
|
---|
405 | * filename The full name of the stored file (up to 100 c.)
|
---|
406 | * mode UNIX permissions in DECIMAL, not octal
|
---|
407 | * uid The Owner ID
|
---|
408 | * gid The Group ID
|
---|
409 | * size Uncompressed filesize
|
---|
410 | * mtime Timestamp of last modification
|
---|
411 | * typeflag Empty for files, set for folders
|
---|
412 | * link For the links, did you guess it ?
|
---|
413 | * uname Owner name
|
---|
414 | * gname Group name
|
---|
415 | */
|
---|
416 | function ListContents() {
|
---|
417 | if(!$this->_nomf) return -3;
|
---|
418 | if(!$this->_OpenRead()) return -4;
|
---|
419 |
|
---|
420 | $result = Array();
|
---|
421 |
|
---|
422 | while ($dat = $this->_read(512)) {
|
---|
423 | $dat = $this->_readHeader($dat);
|
---|
424 | if(!is_array($dat)) continue;
|
---|
425 |
|
---|
426 | $this->_seek(ceil($dat['size']/512)*512,1);
|
---|
427 | $result[] = $dat;
|
---|
428 | }
|
---|
429 |
|
---|
430 | return $result;
|
---|
431 | }
|
---|
432 |
|
---|
433 | /**
|
---|
434 | * Convert a status code into a human readable message
|
---|
435 | *
|
---|
436 | * Some MaxgTar functions like Create(), Add() ... return numerical
|
---|
437 | * status code. You can pass them to this function to grab their english
|
---|
438 | * equivalent.
|
---|
439 | */
|
---|
440 | function TarErrorStr($i) {
|
---|
441 | $ecodes = Array(
|
---|
442 | 1 => true,
|
---|
443 | 0 => "Undocumented error",
|
---|
444 | -1 => "Can't use COMPRESS_GZIP compression : ZLIB extensions are not loaded !",
|
---|
445 | -2 => "Can't use COMPRESS_BZIP compression : BZ2 extensions are not loaded !",
|
---|
446 | -3 => "You must set a archive file to read the contents !",
|
---|
447 | -4 => "Can't open the archive file for read !",
|
---|
448 | -5 => "Invalide file list !",
|
---|
449 | -6 => "Can't open the archive in write mode !",
|
---|
450 | -7 => "There is no ARCHIVE_DYNAMIC to write !",
|
---|
451 | -8 => "Can't create the directory to extract files !",
|
---|
452 | -9 => "Please pass a archive name to send if you made created an ARCHIVE_DYNAMIC !",
|
---|
453 | -10 => "You didn't pass an archive filename and there is no stored ARCHIVE_DYNAMIC !",
|
---|
454 | -11 => "Given archive doesn't exist !"
|
---|
455 | );
|
---|
456 |
|
---|
457 | return isset($ecodes[$i]) ? $ecodes[$i] : $ecodes[0];
|
---|
458 | }
|
---|
459 |
|
---|
460 | function _seek($p_flen, $tell=0) {
|
---|
461 | if($this->_nomf === TarLib::ARCHIVE_DYNAMIC)
|
---|
462 | $this->_memdat=substr($this->_memdat,0,($tell ? strlen($this->_memdat) : 0) + $p_flen);
|
---|
463 | elseif($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
464 | @gzseek($this->_fp, ($tell ? @gztell($this->_fp) : 0)+$p_flen);
|
---|
465 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
466 | @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0)+$p_flen);
|
---|
467 | else
|
---|
468 | @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0)+$p_flen);
|
---|
469 | }
|
---|
470 |
|
---|
471 | function _OpenRead() {
|
---|
472 | if($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
473 | $this->_fp = @gzopen($this->_nomf, 'rb');
|
---|
474 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
475 | $this->_fp = @bzopen($this->_nomf, 'rb');
|
---|
476 | else
|
---|
477 | $this->_fp = @fopen($this->_nomf, 'rb');
|
---|
478 |
|
---|
479 | return ($this->_fp ? true : false);
|
---|
480 | }
|
---|
481 |
|
---|
482 | function _OpenWrite($add = 'w') {
|
---|
483 | if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) return true;
|
---|
484 |
|
---|
485 | if($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
486 | $this->_fp = @gzopen($this->_nomf, $add.'b'.$this->_compzlevel);
|
---|
487 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
488 | $this->_fp = @bzopen($this->_nomf, $add.'b');
|
---|
489 | else
|
---|
490 | $this->_fp = @fopen($this->_nomf, $add.'b');
|
---|
491 |
|
---|
492 | return ($this->_fp ? true : false);
|
---|
493 | }
|
---|
494 |
|
---|
495 | function _CompTar() {
|
---|
496 | if($this->_nomf === TarLib::ARCHIVE_DYNAMIC || !$this->_fp) return;
|
---|
497 |
|
---|
498 | if($this->_comptype == TarLib::COMPRESS_GZIP) @gzclose($this->_fp);
|
---|
499 | elseif($this->_comptype == TarLib::COMPRESS_BZIP) @bzclose($this->_fp);
|
---|
500 | else @fclose($this->_fp);
|
---|
501 | }
|
---|
502 |
|
---|
503 | function _read($p_len) {
|
---|
504 | if($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
505 | return @gzread($this->_fp,$p_len);
|
---|
506 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
507 | return @bzread($this->_fp,$p_len);
|
---|
508 | else
|
---|
509 | return @fread($this->_fp,$p_len);
|
---|
510 | }
|
---|
511 |
|
---|
512 | function _write($p_data) {
|
---|
513 | if($this->_nomf === TarLib::ARCHIVE_DYNAMIC) $this->_memdat .= $p_data;
|
---|
514 | elseif($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
515 | return @gzwrite($this->_fp,$p_data);
|
---|
516 |
|
---|
517 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
518 | return @bzwrite($this->_fp,$p_data);
|
---|
519 |
|
---|
520 | else
|
---|
521 | return @fwrite($this->_fp,$p_data);
|
---|
522 | }
|
---|
523 |
|
---|
524 | function _encode($p_dat) {
|
---|
525 | if($this->_comptype == TarLib::COMPRESS_GZIP)
|
---|
526 | return gzencode($p_dat, $this->_compzlevel);
|
---|
527 | elseif($this->_comptype == TarLib::COMPRESS_BZIP)
|
---|
528 | return bzcompress($p_dat, $this->_compzlevel);
|
---|
529 | else return $p_dat;
|
---|
530 | }
|
---|
531 |
|
---|
532 | function _readHeader($p_dat) {
|
---|
533 | if (!$p_dat || strlen($p_dat) != 512) return false;
|
---|
534 |
|
---|
535 | for ($i=0, $chks=0; $i<148; $i++)
|
---|
536 | $chks += ord($p_dat[$i]);
|
---|
537 |
|
---|
538 | for ($i=156,$chks+=256; $i<512; $i++)
|
---|
539 | $chks += ord($p_dat[$i]);
|
---|
540 |
|
---|
541 | $headers = @unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $p_dat);
|
---|
542 | if(!$headers) return false;
|
---|
543 |
|
---|
544 | $return['checksum'] = OctDec(trim($headers['checksum']));
|
---|
545 | if ($return['checksum'] != $chks) return false;
|
---|
546 |
|
---|
547 | $return['filename'] = trim($headers['filename']);
|
---|
548 | $return['mode'] = OctDec(trim($headers['mode']));
|
---|
549 | $return['uid'] = OctDec(trim($headers['uid']));
|
---|
550 | $return['gid'] = OctDec(trim($headers['gid']));
|
---|
551 | $return['size'] = OctDec(trim($headers['size']));
|
---|
552 | $return['mtime'] = OctDec(trim($headers['mtime']));
|
---|
553 | $return['typeflag'] = $headers['typeflag'];
|
---|
554 | $return['link'] = trim($headers['link']);
|
---|
555 | $return['uname'] = trim($headers['uname']);
|
---|
556 | $return['gname'] = trim($headers['gname']);
|
---|
557 |
|
---|
558 | return $return;
|
---|
559 | }
|
---|
560 |
|
---|
561 | function _fetchFilelist($p_filelist) {
|
---|
562 | if(!$p_filelist || (is_array($p_filelist) && !@count($p_filelist))) return false;
|
---|
563 |
|
---|
564 | if(is_string($p_filelist)) {
|
---|
565 | $p_filelist = explode('|',$p_filelist);
|
---|
566 | if(!is_array($p_filelist)) $p_filelist = Array($p_filelist);
|
---|
567 | }
|
---|
568 |
|
---|
569 | return $p_filelist;
|
---|
570 | }
|
---|
571 |
|
---|
572 | function _addFileList($p_fl, $p_addir, $p_remdir) {
|
---|
573 | foreach($p_fl as $file) {
|
---|
574 | if(($file == $this->_nomf && $this->_nomf != TarLib::ARCHIVE_DYNAMIC) || !$file || (!file_exists($file) && !is_array($file)))
|
---|
575 | continue;
|
---|
576 |
|
---|
577 | if (!$this->_addFile($file, $p_addir, $p_remdir))
|
---|
578 | continue;
|
---|
579 |
|
---|
580 | if (@is_dir($file)) {
|
---|
581 | $d = @opendir($file);
|
---|
582 |
|
---|
583 | if(!$d) continue;
|
---|
584 | readdir($d);
|
---|
585 | readdir($d);
|
---|
586 |
|
---|
587 | while($f = readdir($d)) {
|
---|
588 | if($file != ".") $tmplist[0] = "$file/$f";
|
---|
589 | else $tmplist[0] = $d;
|
---|
590 |
|
---|
591 | $this->_addFileList($tmplist, $p_addir, $p_remdir);
|
---|
592 | }
|
---|
593 |
|
---|
594 | closedir($d);
|
---|
595 | unset($tmplist,$f);
|
---|
596 | }
|
---|
597 | }
|
---|
598 | return true;
|
---|
599 | }
|
---|
600 |
|
---|
601 | function _addFile($p_fn, $p_addir = '', $p_remdir = '') {
|
---|
602 | if(is_array($p_fn)) list($p_fn, $data) = $p_fn;
|
---|
603 | $sname = $p_fn;
|
---|
604 |
|
---|
605 | if($p_remdir) {
|
---|
606 | if(substr($p_remdir,-1) != '/') $p_remdir .= "/";
|
---|
607 |
|
---|
608 | if(substr($sname, 0, strlen($p_remdir)) == $p_remdir)
|
---|
609 | $sname = substr($sname, strlen($p_remdir));
|
---|
610 | }
|
---|
611 |
|
---|
612 | if($p_addir) $sname = $p_addir.(substr($p_addir,-1) == '/' ? '' : "/").$sname;
|
---|
613 |
|
---|
614 | if(strlen($sname) > 99) return;
|
---|
615 |
|
---|
616 | if(@is_dir($p_fn)) {
|
---|
617 | if(!$this->_writeFileHeader($p_fn, $sname)) return false;
|
---|
618 | } else {
|
---|
619 | if(!$data) {
|
---|
620 | $fp = fopen($p_fn, 'rb');
|
---|
621 | if(!$fp) return false;
|
---|
622 | }
|
---|
623 |
|
---|
624 | if(!$this->_writeFileHeader($p_fn, $sname, ($data ? strlen($data) : false))) return false;
|
---|
625 |
|
---|
626 | if(!$data) {
|
---|
627 | while(!feof($fp)) {
|
---|
628 | $packed = pack("a512", fread($fp,512));
|
---|
629 | $this->_write($packed);
|
---|
630 | }
|
---|
631 | fclose($fp);
|
---|
632 | } else {
|
---|
633 | $len = strlen($data);
|
---|
634 | for($s = 0; $s < $len; $s += 512){
|
---|
635 | $this->_write(pack("a512",substr($data,$s,512)));
|
---|
636 | }
|
---|
637 | }
|
---|
638 | }
|
---|
639 |
|
---|
640 | return true;
|
---|
641 | }
|
---|
642 |
|
---|
643 | function _writeFileHeader($p_file, $p_sname, $p_data=false) {
|
---|
644 | if(!$p_data) {
|
---|
645 | if (!$p_sname) $p_sname = $p_file;
|
---|
646 | $p_sname = $this->_pathTrans($p_sname);
|
---|
647 |
|
---|
648 | $h_info = stat($p_file);
|
---|
649 | $h[0] = sprintf("%6s ", DecOct($h_info[4]));
|
---|
650 | $h[] = sprintf("%6s ", DecOct($h_info[5]));
|
---|
651 | $h[] = sprintf("%6s ", DecOct(fileperms($p_file)));
|
---|
652 | clearstatcache();
|
---|
653 | $h[] = sprintf("%11s ", DecOct(filesize($p_file)));
|
---|
654 | $h[] = sprintf("%11s", DecOct(filemtime($p_file)));
|
---|
655 |
|
---|
656 | $dir = @is_dir($p_file) ? '5' : '';
|
---|
657 | } else {
|
---|
658 | $dir = '';
|
---|
659 | $p_data = sprintf("%11s ", DecOct($p_data));
|
---|
660 | $time = sprintf("%11s ", DecOct(time()));
|
---|
661 | $h = Array(" 0 "," 0 "," 40777 ",$p_data,$time);
|
---|
662 | }
|
---|
663 |
|
---|
664 | $data_first = pack("a100a8a8a8a12A12", $p_sname, $h[2], $h[0], $h[1], $h[3], $h[4]);
|
---|
665 | $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', "");
|
---|
666 |
|
---|
667 | for ($i=0,$chks=0; $i<148; $i++)
|
---|
668 | $chks += ord($data_first[$i]);
|
---|
669 |
|
---|
670 | for ($i=156, $chks+=256, $j=0; $i<512; $i++, $j++)
|
---|
671 | $chks += ord($data_last[$j]);
|
---|
672 |
|
---|
673 | $this->_write($data_first);
|
---|
674 |
|
---|
675 | $chks = pack("a8",sprintf("%6s ", DecOct($chks)));
|
---|
676 | $this->_write($chks.$data_last);
|
---|
677 |
|
---|
678 | return true;
|
---|
679 | }
|
---|
680 |
|
---|
681 | function _append($p_filelist, $p_addir="", $p_remdir="") {
|
---|
682 | if(!$this->_fp) if(!$this->_OpenWrite('a')) return -6;
|
---|
683 |
|
---|
684 | if($this->_nomf == TarLib::ARCHIVE_DYNAMIC) {
|
---|
685 | $s = strlen($this->_memdat);
|
---|
686 | $this->_memdat = substr($this->_memdat,0,-512);
|
---|
687 | } else {
|
---|
688 | $s = filesize($this->_nomf);
|
---|
689 | $this->_seek($s-512);
|
---|
690 | }
|
---|
691 |
|
---|
692 | $ok = $this->_addFileList($p_filelist, $p_addir, $p_remdir);
|
---|
693 | $this->_writeFooter();
|
---|
694 |
|
---|
695 | return $ok;
|
---|
696 | }
|
---|
697 |
|
---|
698 | function _pathTrans($p_dir) {
|
---|
699 | if ($p_dir) {
|
---|
700 | $subf = explode("/", $p_dir);
|
---|
701 | $r='';
|
---|
702 |
|
---|
703 | for ($i=count($subf)-1; $i>=0; $i--) {
|
---|
704 | if ($subf[$i] == ".") {
|
---|
705 | # do nothing
|
---|
706 | } elseif ($subf[$i] == "..") {
|
---|
707 | $i--;
|
---|
708 | } elseif (!$subf[$i] && $i!=count($subf)-1 && $i) {
|
---|
709 | # do nothing
|
---|
710 | } else {
|
---|
711 | $r = $subf[$i].($i!=(count($subf)-1) ? "/".$r : "");
|
---|
712 | }
|
---|
713 | }
|
---|
714 | }
|
---|
715 | return $r;
|
---|
716 | }
|
---|
717 |
|
---|
718 | function _writeFooter() {
|
---|
719 | $this->_write(pack("a512", ""));
|
---|
720 | }
|
---|
721 |
|
---|
722 | function _extractList($p_to, $p_files, $p_remdir, $p_mode = 0755) {
|
---|
723 | if (!$p_to || ($p_to[0]!="/"&&substr($p_to,0,3)!="../"&&substr($p_to,1,3)!=":\\"&&substr($p_to,1,2)!=":/")) /*" // <- PHP Coder bug */
|
---|
724 | $p_to = "./$p_to";
|
---|
725 |
|
---|
726 | if ($p_remdir && substr($p_remdir,-1)!='/') $p_remdir .= '/';
|
---|
727 | $p_remdirs = strlen($p_remdir);
|
---|
728 | while($dat = $this->_read(512)) {
|
---|
729 | $headers = $this->_readHeader($dat);
|
---|
730 | if(!$headers['filename']) continue;
|
---|
731 |
|
---|
732 | if($p_files == -1 || $p_files[0] == -1){
|
---|
733 | $extract = true;
|
---|
734 | } else {
|
---|
735 | $extract = false;
|
---|
736 |
|
---|
737 | foreach($p_files as $f) {
|
---|
738 | if(substr($f,-1) == "/") {
|
---|
739 | if((strlen($headers['filename']) > strlen($f)) && (substr($headers['filename'],0,strlen($f))==$f)) {
|
---|
740 | $extract = true;
|
---|
741 | break;
|
---|
742 | }
|
---|
743 | } elseif($f == $headers['filename']) {
|
---|
744 | $extract = true;
|
---|
745 | break;
|
---|
746 | }
|
---|
747 | }
|
---|
748 | }
|
---|
749 |
|
---|
750 | if ($extract) {
|
---|
751 | $det[] = $headers;
|
---|
752 | if ($p_remdir && substr($headers['filename'],0,$p_remdirs)==$p_remdir)
|
---|
753 | $headers['filename'] = substr($headers['filename'],$p_remdirs);
|
---|
754 |
|
---|
755 | if($headers['filename'].'/' == $p_remdir && $headers['typeflag']=='5') continue;
|
---|
756 |
|
---|
757 | if ($p_to != "./" && $p_to != "/") {
|
---|
758 | while($p_to{-1}=="/") $p_to = substr($p_to,0,-1);
|
---|
759 |
|
---|
760 | if($headers['filename']{0} == "/")
|
---|
761 | $headers['filename'] = $p_to.$headers['filename'];
|
---|
762 | else
|
---|
763 | $headers['filename'] = $p_to."/".$headers['filename'];
|
---|
764 | }
|
---|
765 |
|
---|
766 | $ok = $this->_dirApp($headers['typeflag']=="5" ? $headers['filename'] : dirname($headers['filename']));
|
---|
767 | if($ok < 0) return $ok;
|
---|
768 |
|
---|
769 | if (!$headers['typeflag']) {
|
---|
770 | if (!$fp = @fopen($headers['filename'], "wb")) return -6;
|
---|
771 | $n = floor($headers['size']/512);
|
---|
772 |
|
---|
773 | for ($i=0; $i<$n; $i++){
|
---|
774 | fwrite($fp, $this->_read(512),512);
|
---|
775 | }
|
---|
776 | if (($headers['size'] % 512) != 0) fwrite($fp, $this->_read(512), $headers['size'] % 512);
|
---|
777 |
|
---|
778 | fclose($fp);
|
---|
779 | touch($headers['filename'], $headers['mtime']);
|
---|
780 | chmod($headers['filename'], $p_mode);
|
---|
781 | } else {
|
---|
782 | $this->_seek(ceil($headers['size']/512)*512,1);
|
---|
783 | }
|
---|
784 | }else $this->_seek(ceil($headers['size']/512)*512,1);
|
---|
785 | }
|
---|
786 | return $det;
|
---|
787 | }
|
---|
788 |
|
---|
789 | function _dirApp($d) {
|
---|
790 | // map to dokuwiki function (its more robust)
|
---|
791 | return io_mkdir_p($d);
|
---|
792 | }
|
---|
793 |
|
---|
794 | }
|
---|
795 |
|
---|