1: | <?php
|
2: |
|
3: |
|
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: |
|
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | |
47: | |
48: | |
49: | |
50: | |
51: | |
52: | |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | |
71: | |
72: | |
73: | |
74: | |
75: | |
76: | |
77: |
|
78: |
|
79: | |
80: | |
81: | |
82: | |
83: | |
84: | |
85: | |
86: | |
87: | |
88: | |
89: | |
90: | |
91: |
|
92: | class Tar
|
93: | {
|
94: | |
95: | |
96: | |
97: |
|
98: | public $filename;
|
99: | public $isGzipped;
|
100: | public $tar_file;
|
101: | |
102: | |
103: |
|
104: |
|
105: | |
106: | |
107: | |
108: |
|
109: | public $files;
|
110: | public $directories;
|
111: | public $numFiles;
|
112: | public $numDirectories;
|
113: | |
114: | |
115: |
|
116: |
|
117: | |
118: | |
119: |
|
120: | public function __construct()
|
121: | {
|
122: | }
|
123: |
|
124: | |
125: | |
126: | |
127: | |
128: | |
129: | |
130: | |
131: | |
132: |
|
133: | public function __computeUnsignedChecksum($bytestring)
|
134: | {
|
135: | $unsigned_chksum = '';
|
136: | for ($i = 0; $i < 512; ++$i) {
|
137: | $unsigned_chksum += ord($bytestring[$i]);
|
138: | }
|
139: | for ($i = 0; $i < 8; ++$i) {
|
140: | $unsigned_chksum -= ord($bytestring[148 + $i]);
|
141: | $unsigned_chksum += ord(' ') * 8;
|
142: | }
|
143: |
|
144: | return $unsigned_chksum;
|
145: | }
|
146: |
|
147: | |
148: | |
149: | |
150: | |
151: | |
152: | |
153: |
|
154: | public function __parseNullPaddedString($string)
|
155: | {
|
156: | $position = strpos($string, chr(0));
|
157: |
|
158: | return substr($string, 0, $position);
|
159: | }
|
160: |
|
161: | |
162: | |
163: | |
164: | |
165: | |
166: |
|
167: | public function __parseTar()
|
168: | {
|
169: |
|
170: | $tar_length = strlen($this->tar_file);
|
171: | $main_offset = 0;
|
172: | $this->numFiles = 0;
|
173: | while ($main_offset < $tar_length) {
|
174: |
|
175: | if (substr($this->tar_file, $main_offset, 512) == str_repeat(chr(0), 512)) {
|
176: | break;
|
177: | }
|
178: |
|
179: | $file_name = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset, 100));
|
180: |
|
181: | $file_mode = substr($this->tar_file, $main_offset + 100, 8);
|
182: |
|
183: | $file_uid = octdec(substr($this->tar_file, $main_offset + 108, 8));
|
184: |
|
185: | $file_gid = octdec(substr($this->tar_file, $main_offset + 116, 8));
|
186: |
|
187: | $file_size = octdec(substr($this->tar_file, $main_offset + 124, 12));
|
188: |
|
189: | $file_time = octdec(substr($this->tar_file, $main_offset + 136, 12));
|
190: |
|
191: | $file_chksum = octdec(substr($this->tar_file, $main_offset + 148, 6));
|
192: |
|
193: | $file_uname = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset + 265, 32));
|
194: |
|
195: | $file_gname = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset + 297, 32));
|
196: |
|
197: | if ($this->__computeUnsignedChecksum(substr($this->tar_file, $main_offset, 512)) != $file_chksum) {
|
198: | return false;
|
199: | }
|
200: |
|
201: | $file_contents = substr($this->tar_file, $main_offset + 512, $file_size);
|
202: |
|
203: | |
204: | |
205: | |
206: | |
207: | |
208: | |
209: | |
210: | |
211: | |
212: | |
213: |
|
214: |
|
215: | if ($file_size > 0) {
|
216: |
|
217: | $this->numFiles++;
|
218: |
|
219: | $activeFile =& $this->files[];
|
220: |
|
221: | $activeFile['name'] = $file_name;
|
222: | $activeFile['mode'] = $file_mode;
|
223: | $activeFile['size'] = $file_size;
|
224: | $activeFile['time'] = $file_time;
|
225: | $activeFile['user_id'] = $file_uid;
|
226: | $activeFile['group_id'] = $file_gid;
|
227: | $activeFile['user_name'] = $file_uname;
|
228: | $activeFile['group_name'] = $file_gname;
|
229: | $activeFile['checksum'] = $file_chksum;
|
230: | $activeFile['file'] = $file_contents;
|
231: | } else {
|
232: |
|
233: | $this->numDirectories++;
|
234: |
|
235: | $activeDir =& $this->directories[];
|
236: |
|
237: | $activeDir['name'] = $file_name;
|
238: | $activeDir['mode'] = $file_mode;
|
239: | $activeDir['time'] = $file_time;
|
240: | $activeDir['user_id'] = $file_uid;
|
241: | $activeDir['group_id'] = $file_gid;
|
242: | $activeDir['user_name'] = $file_uname;
|
243: | $activeDir['group_name'] = $file_gname;
|
244: | $activeDir['checksum'] = $file_chksum;
|
245: | }
|
246: |
|
247: | $main_offset += 512 + (ceil($file_size / 512) * 512);
|
248: | }
|
249: |
|
250: | return true;
|
251: | }
|
252: |
|
253: | |
254: | |
255: | |
256: | |
257: | |
258: | |
259: |
|
260: | public function __readTar($filename = '')
|
261: | {
|
262: |
|
263: | if (!$filename) {
|
264: | $filename = $this->filename;
|
265: | }
|
266: |
|
267: | $fp = fopen($filename, 'rb');
|
268: | $this->tar_file = fread($fp, filesize($filename));
|
269: | fclose($fp);
|
270: |
|
271: | if ($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) {
|
272: | if (!function_exists('gzinflate')) {
|
273: | return false;
|
274: | }
|
275: | $this->isGzipped = true;
|
276: | $this->tar_file = gzinflate(substr($this->tar_file, 10, -4));
|
277: | }
|
278: |
|
279: | $this->__parseTar();
|
280: |
|
281: | return true;
|
282: | }
|
283: |
|
284: | |
285: | |
286: | |
287: | |
288: | |
289: |
|
290: | public function __generateTar()
|
291: | {
|
292: |
|
293: | unset($this->tar_file);
|
294: |
|
295: | if ($this->numDirectories > 0) {
|
296: | foreach ($this->directories as $key => $information) {
|
297: | unset($header);
|
298: |
|
299: |
|
300: | $header .= str_pad($information['name'], 100, chr(0));
|
301: | $header .= str_pad(decoct($information['mode']), 7, '0', STR_PAD_LEFT) . chr(0);
|
302: | $header .= str_pad(decoct($information['user_id']), 7, '0', STR_PAD_LEFT) . chr(0);
|
303: | $header .= str_pad(decoct($information['group_id']), 7, '0', STR_PAD_LEFT) . chr(0);
|
304: | $header .= str_pad(decoct(0), 11, '0', STR_PAD_LEFT) . chr(0);
|
305: | $header .= str_pad(decoct($information['time']), 11, '0', STR_PAD_LEFT) . chr(0);
|
306: | $header .= str_repeat(' ', 8);
|
307: | $header .= '5';
|
308: | $header .= str_repeat(chr(0), 100);
|
309: | $header .= str_pad('ustar', 6, chr(32));
|
310: | $header .= chr(32) . chr(0);
|
311: | $header .= str_pad('', 32, chr(0));
|
312: | $header .= str_pad('', 32, chr(0));
|
313: | $header .= str_repeat(chr(0), 8);
|
314: | $header .= str_repeat(chr(0), 8);
|
315: | $header .= str_repeat(chr(0), 155);
|
316: | $header .= str_repeat(chr(0), 12);
|
317: |
|
318: | $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)), 6, '0', STR_PAD_LEFT);
|
319: | for ($i = 0; $i < 6; ++$i) {
|
320: | $header[148 + $i] = substr($checksum, $i, 1);
|
321: | }
|
322: | $header[154] = chr(0);
|
323: | $header[155] = chr(32);
|
324: |
|
325: | $this->tar_file .= $header;
|
326: | }
|
327: | }
|
328: |
|
329: | if ($this->numFiles > 0) {
|
330: | $this->tar_file = '';
|
331: | foreach ($this->files as $key => $information) {
|
332: | unset($header);
|
333: |
|
334: |
|
335: | $header = str_pad($information['name'], 100, chr(0));
|
336: | $header .= str_pad(decoct($information['mode']), 7, '0', STR_PAD_LEFT) . chr(0);
|
337: | $header .= str_pad(decoct($information['user_id']), 7, '0', STR_PAD_LEFT) . chr(0);
|
338: | $header .= str_pad(decoct($information['group_id']), 7, '0', STR_PAD_LEFT) . chr(0);
|
339: | $header .= str_pad(decoct($information['size']), 11, '0', STR_PAD_LEFT) . chr(0);
|
340: | $header .= str_pad(decoct($information['time']), 11, '0', STR_PAD_LEFT) . chr(0);
|
341: | $header .= str_repeat(' ', 8);
|
342: | $header .= '0';
|
343: | $header .= str_repeat(chr(0), 100);
|
344: | $header .= str_pad('ustar', 6, chr(32));
|
345: | $header .= chr(32) . chr(0);
|
346: | $header .= str_pad($information['user_name'], 32, chr(0));
|
347: | $header .= str_pad($information['group_name'], 32, chr(0));
|
348: | $header .= str_repeat(chr(0), 8);
|
349: | $header .= str_repeat(chr(0), 8);
|
350: | $header .= str_repeat(chr(0), 155);
|
351: | $header .= str_repeat(chr(0), 12);
|
352: |
|
353: | $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)), 6, '0', STR_PAD_LEFT);
|
354: | for ($i = 0; $i < 6; ++$i) {
|
355: | $header[148 + $i] = substr($checksum, $i, 1);
|
356: | }
|
357: | $header[154] = chr(0);
|
358: | $header[155] = chr(32);
|
359: |
|
360: | $file_contents = str_pad($information['file'], ceil($information['size'] / 512) * 512, chr(0));
|
361: |
|
362: | $this->tar_file .= $header . $file_contents;
|
363: | }
|
364: | }
|
365: |
|
366: | $this->tar_file .= str_repeat(chr(0), 512);
|
367: |
|
368: | return true;
|
369: | }
|
370: |
|
371: | |
372: | |
373: | |
374: | |
375: | |
376: |
|
377: | public function openTAR($filename)
|
378: | {
|
379: |
|
380: | unset($this->filename, $this->isGzipped, $this->tar_file, $this->files, $this->directories, $this->numFiles, $this->numDirectories);
|
381: |
|
382: |
|
383: | if (!file_exists($filename)) {
|
384: | return false;
|
385: | }
|
386: |
|
387: | $this->filename = $filename;
|
388: |
|
389: | $this->__readTar();
|
390: |
|
391: | return true;
|
392: | }
|
393: |
|
394: | |
395: | |
396: | |
397: | |
398: | |
399: |
|
400: | public function appendTar($filename)
|
401: | {
|
402: |
|
403: | if (!file_exists($filename)) {
|
404: | return false;
|
405: | }
|
406: | $this->__readTar($filename);
|
407: |
|
408: | return true;
|
409: | }
|
410: |
|
411: | |
412: | |
413: | |
414: | |
415: | |
416: |
|
417: | public function getFile($filename)
|
418: | {
|
419: | if ($this->numFiles > 0) {
|
420: | foreach ($this->files as $key => $information) {
|
421: | if ($information['name'] == $filename) {
|
422: | return $information;
|
423: | }
|
424: | }
|
425: | }
|
426: |
|
427: | return false;
|
428: | }
|
429: |
|
430: | |
431: | |
432: | |
433: | |
434: | |
435: |
|
436: | public function getDirectory($dirname)
|
437: | {
|
438: | if ($this->numDirectories > 0) {
|
439: | foreach ($this->directories as $key => $information) {
|
440: | if ($information['name'] == $dirname) {
|
441: | return $information;
|
442: | }
|
443: | }
|
444: | }
|
445: |
|
446: | return false;
|
447: | }
|
448: |
|
449: | |
450: | |
451: | |
452: | |
453: | |
454: |
|
455: | public function containsFile($filename)
|
456: | {
|
457: | if ($this->numFiles > 0) {
|
458: | foreach ($this->files as $key => $information) {
|
459: | if ($information['name'] == $filename) {
|
460: | return true;
|
461: | }
|
462: | }
|
463: | }
|
464: |
|
465: | return false;
|
466: | }
|
467: |
|
468: | |
469: | |
470: | |
471: | |
472: | |
473: |
|
474: | public function containsDirectory($dirname)
|
475: | {
|
476: | if ($this->numDirectories > 0) {
|
477: | foreach ($this->directories as $key => $information) {
|
478: | if ($information['name'] == $dirname) {
|
479: | return true;
|
480: | }
|
481: | }
|
482: | }
|
483: |
|
484: | return false;
|
485: | }
|
486: |
|
487: | |
488: | |
489: | |
490: | |
491: | |
492: |
|
493: | public function addDirectory($dirname)
|
494: | {
|
495: | if (!file_exists($dirname)) {
|
496: | return false;
|
497: | }
|
498: |
|
499: | $file_information = stat($dirname);
|
500: |
|
501: | $this->numDirectories++;
|
502: | $activeDir =& $this->directories[];
|
503: | $activeDir['name'] = $dirname;
|
504: | $activeDir['mode'] = $file_information['mode'];
|
505: | $activeDir['time'] = $file_information['time'];
|
506: | $activeDir['user_id'] = $file_information['uid'];
|
507: | $activeDir['group_id'] = $file_information['gid'];
|
508: | $activeDir['checksum'] = $checksum;
|
509: |
|
510: | return true;
|
511: | }
|
512: |
|
513: | |
514: | |
515: | |
516: | |
517: | |
518: | |
519: |
|
520: | public function addFile($filename, $binary = false)
|
521: | {
|
522: |
|
523: | if (!file_exists($filename)) {
|
524: | return false;
|
525: | }
|
526: |
|
527: | if ($this->containsFile($filename)) {
|
528: | return false;
|
529: | }
|
530: |
|
531: | $file_information = stat($filename);
|
532: |
|
533: | if (!$binary) {
|
534: | $fp = fopen($filename, 'r');
|
535: | } else {
|
536: | $fp = fopen($filename, 'rb');
|
537: | }
|
538: | $file_contents = fread($fp, filesize($filename));
|
539: | fclose($fp);
|
540: |
|
541: | $this->numFiles++;
|
542: | $activeFile =& $this->files[];
|
543: | $activeFile['name'] = $filename;
|
544: | $activeFile['mode'] = $file_information['mode'];
|
545: | $activeFile['user_id'] = $file_information['uid'];
|
546: | $activeFile['group_id'] = $file_information['gid'];
|
547: | $activeFile['size'] = $file_information['size'];
|
548: | $activeFile['time'] = $file_information['mtime'];
|
549: | $activeFile['checksum'] = isset($checksum) ? $checksum : '';
|
550: | $activeFile['user_name'] = '';
|
551: | $activeFile['group_name'] = '';
|
552: | $activeFile['file'] = trim($file_contents);
|
553: |
|
554: | return true;
|
555: | }
|
556: |
|
557: | |
558: | |
559: | |
560: | |
561: | |
562: |
|
563: | public function removeFile($filename)
|
564: | {
|
565: | if ($this->numFiles > 0) {
|
566: | foreach ($this->files as $key => $information) {
|
567: | if ($information['name'] == $filename) {
|
568: | $this->numFiles--;
|
569: | unset($this->files[$key]);
|
570: |
|
571: | return true;
|
572: | }
|
573: | }
|
574: | }
|
575: |
|
576: | return false;
|
577: | }
|
578: |
|
579: | |
580: | |
581: | |
582: | |
583: | |
584: |
|
585: | public function removeDirectory($dirname)
|
586: | {
|
587: | if ($this->numDirectories > 0) {
|
588: | foreach ($this->directories as $key => $information) {
|
589: | if ($information['name'] == $dirname) {
|
590: | $this->numDirectories--;
|
591: | unset($this->directories[$key]);
|
592: |
|
593: | return true;
|
594: | }
|
595: | }
|
596: | }
|
597: |
|
598: | return false;
|
599: | }
|
600: |
|
601: | |
602: | |
603: | |
604: | |
605: |
|
606: | public function saveTar()
|
607: | {
|
608: | if (!$this->filename) {
|
609: | return false;
|
610: | }
|
611: |
|
612: | $this->toTar($this->filename, $this->isGzipped);
|
613: |
|
614: | return true;
|
615: | }
|
616: |
|
617: | |
618: | |
619: | |
620: | |
621: | |
622: | |
623: |
|
624: | public function toTar($filename, $useGzip)
|
625: | {
|
626: | if (!$filename) {
|
627: | return false;
|
628: | }
|
629: |
|
630: | $this->__generateTar();
|
631: |
|
632: | if ($useGzip) {
|
633: |
|
634: | if (!function_exists('gzencode')) {
|
635: | return false;
|
636: | }
|
637: | $file = gzencode($this->tar_file);
|
638: | } else {
|
639: | $file = $this->tar_file;
|
640: | }
|
641: |
|
642: | $fp = fopen($filename, 'wb');
|
643: | fwrite($fp, $file);
|
644: | fclose($fp);
|
645: |
|
646: | return true;
|
647: | }
|
648: |
|
649: | |
650: | |
651: | |
652: | |
653: | |
654: | |
655: |
|
656: | public function toTarOutput($filename, $useGzip)
|
657: | {
|
658: | if (!$filename) {
|
659: | return false;
|
660: | }
|
661: |
|
662: | $this->__generateTar();
|
663: |
|
664: | if ($useGzip) {
|
665: |
|
666: | if (!function_exists('gzencode')) {
|
667: | return false;
|
668: | }
|
669: | $file = gzencode($this->tar_file);
|
670: | } else {
|
671: | $file = $this->tar_file;
|
672: | }
|
673: |
|
674: | return $file;
|
675: | }
|
676: | }
|
677: | |