1<?php
2// stolen from PEAR2_Pyrus_Developer_Creator_Tar by Greg Beaver, the original author, for use in unit tests
3class corrupt_tarmaker
4{
5    /**
6     * Path to archive file
7     *
8     * @var string
9     */
10    protected $archive;
11    /**
12     * Temporary stream used for creating the archive
13     *
14     * @var stream
15     */
16    protected $tmp;
17    protected $path;
18    protected $compress;
19    function __construct($path, $compress = 'zlib')
20    {
21        $this->compress = $compress;
22        if ($compress === 'bz2' && !function_exists('bzopen')) {
23            throw new PEAR2_Pyrus_Developer_Creator_Exception(
24                'bzip2 extension not available');
25        }
26        if ($compress === 'zlib' && !function_exists('gzopen')) {
27            throw new PEAR2_Pyrus_Developer_Creator_Exception(
28                'zlib extension not available');
29        }
30        $this->path = $path;
31    }
32
33    /**
34     * save a file inside this package
35     *
36     * This code is modified from Vincent Lascaux's File_Archive
37     * package, which is licensed under the LGPL license.
38     * @param string relative path within the package
39     * @param string|resource file contents or open file handle
40     */
41    function addFile($path, $fileOrStream, $stat = null, $corrupt = null)
42    {
43        clearstatcache();
44        if ($stat === null) {
45            if (is_resource($fileOrStream)) {
46                $stat = fstat($fileOrStream);
47            } else {
48                $stat = array(
49                    'mode' => 0x8000 + 0644,
50                    'uid' => 0,
51                    'gid' => 0,
52                    'size' => strlen($fileOrStream),
53                    'mtime' => time(),
54                );
55            }
56        }
57
58        $link = null;
59        if ($stat['mode'] & 0xA000 && $corrupt === 'symlink') {
60            $type = 2;        // Symbolic Link
61            $link = $fileOrStream;
62            $stat['size'] = 0;
63            $fileOrStream = '';
64        } else if ($stat['mode'] & 0xA000) {
65            $type = 1;        // Link
66            $link = $fileOrStream;
67            $stat['size'] = 0;
68            $fileOrStream = '';
69        } else if ($stat['mode'] & 0x4000) {
70            $type = 5;        // Directory
71        } else if ($stat['mode'] & 0x8000) {
72            $type = 0;        // Regular
73        } else {
74            $type = 9;        // Unknown
75        }
76
77        $filePrefix = '';
78        if (strlen($path) > 255) {
79            throw new Exception(
80                "$path is too long, must be 255 characters or less"
81            );
82        } else if (strlen($path) > 100) {
83            $filePrefix = substr($path, 0, strlen($path)-100);
84            $path = substr($path, -100);
85        }
86
87        $block = pack('a100a8a8a8a12A12',
88                $path,
89                decoct($stat['mode']),
90                sprintf('%6s ',decoct($stat['uid'])),
91                sprintf('%6s ',decoct($stat['gid'])),
92                sprintf('%11s ',decoct($stat['size'])),
93                sprintf('%11s ',decoct($stat['mtime']))
94            );
95
96        $blockend = pack('a1a100a6a2a32a32a8a8a155a12',
97            $type,
98            $link,
99            'ustar',
100            '00',
101            'Pyrus',
102            'Pyrus',
103            '',
104            '',
105            $filePrefix,
106            '');
107
108        $checkheader = array_merge(str_split($block), str_split($blockend));
109        if (!function_exists('_pear2tarchecksum')) {
110            function _pear2tarchecksum($a, $b) {return $a + ord($b);}
111        }
112        $checksum = 256; // 8 * ord(' ');
113        $checksum += array_reduce($checkheader, '_pear2tarchecksum');
114
115    if ($corrupt === 'checksum') $checksum++;
116        $checksum = pack('a8', sprintf('%6s ', decoct($checksum)));
117
118        fwrite($this->tmp, $block . $checksum . $blockend, 512);
119        if (is_resource($fileOrStream)) {
120            stream_copy_to_stream($fileOrStream, $this->tmp);
121            if ($stat['size'] % 512) {
122                fwrite($this->tmp, str_repeat("\0", 512 - $stat['size'] % 512));
123            }
124        } else {
125            fwrite($this->tmp, $fileOrStream);
126            if (strlen($fileOrStream) && !isset($link) && strlen($fileOrStream) % 512) {
127                fwrite($this->tmp, str_repeat("\0", 512 - strlen($fileOrStream) % 512));
128            }
129        }
130    }
131
132    /**
133     * Initialize the package creator
134     */
135    function init()
136    {
137        switch ($this->compress) {
138            case 'zlib' :
139                $this->tmp = gzopen($this->path, 'wb');
140                break;
141            case 'bz2' :
142                $this->tmp = bzopen($this->path, 'w');
143                break;
144            case 'none' :
145                $this->tmp = fopen($this->path, 'wb');
146                break;
147            default :
148                throw new Exception(
149                    'unknown compression type ' . $this->compress);
150        }
151    }
152
153    /**
154     * Create an internal directory, creating parent directories as needed
155     *
156     * @param string $dir
157     */
158    function mkdir($dir)
159    {
160        $this->addFile($dir, "", array(
161                    'mode' => 0x4000 + 0644,
162                    'uid' => 0,
163                    'gid' => 0,
164                    'size' => 0,
165                    'mtime' => time(),
166                ));
167    }
168
169    /**
170     * Finish saving the package
171     */
172    function close()
173    {
174        fwrite($this->tmp, pack('a1024', ''));
175        fclose($this->tmp);
176    }
177}
178