1: <?php
2: // $Id$
3:
4: /**
5: * package::i.tools
6: *
7: * php-downloader v1.0 - www.ipunkt.biz
8: *
9: * (c) 2002 - www.ipunkt.biz (rok)
10: *
11: * Zip file creation class.
12: * Makes zip files.
13: *
14: * Based on :
15: *
16: * http://www.zend.com/codex.php?id=535&single=1
17: * By Eric Mueller <eric@themepark.com>
18: *
19: * http://www.zend.com/codex.php?id=470&single=1
20: * by Denis125 <webmaster@atlant.ru>
21: *
22: * a patch from Peter Listiak <mlady@users.sourceforge.net> for last modified
23: * date and time of the compressed file
24: *
25: * Official ZIP file format: http://www.pkware.com/appnote.txt
26: *
27: * @copyright (c) 2002 - www.ipunkt.biz (rok)
28: * @package class
29: */
30: class zipfile
31: {
32: /**
33: * Array to store compressed data
34: *
35: * @var array $datasec
36: */
37: public $datasec = array();
38:
39: /**
40: * Central directory
41: *
42: * @var array $ctrl_dir
43: */
44: public $ctrl_dir = array();
45:
46: /**
47: * End of central directory record
48: *
49: * @var string $eof_ctrl_dir
50: */
51: public $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
52:
53: /**
54: * Last offset position
55: *
56: * @var integer $old_offset
57: */
58: public $old_offset = 0;
59:
60: /**
61: * Converts an Unix timestamp to a four byte DOS date and time format (date
62: * in high two bytes, time in low two bytes allowing magnitude comparison).
63: *
64: * @param integer $unixtime the current Unix timestamp
65: * @return integer the current date in a four byte DOS format
66: * @access private
67: */
68: public function unix2DosTime($unixtime = 0)
69: {
70: $timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
71: if ($timearray['year'] < 1980) {
72: $timearray['year'] = 1980;
73: $timearray['mon'] = 1;
74: $timearray['mday'] = 1;
75: $timearray['hours'] = 0;
76: $timearray['minutes'] = 0;
77: $timearray['seconds'] = 0;
78: } // end if
79: return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
80: } // end of the 'unix2DosTime()' method
81:
82: /**
83: * Adds "file" to archive
84: *
85: * @param string $data file contents
86: * @param string $name name of the file in the archive (may contains the path)
87: * @param integer $time the current timestamp
88: * @access public
89: */
90: public function addFile($data, $name, $time = 0)
91: {
92: $name = str_replace('\\', '/', $name);
93:
94: $dtime = dechex($this->unix2DosTime($time));
95: $hexdtime = '\x' . $dtime[6] . $dtime[7] . '\x' . $dtime[4] . $dtime[5] . '\x' . $dtime[2] . $dtime[3] . '\x' . $dtime[0] . $dtime[1];
96: eval('$hexdtime = "' . $hexdtime . '";');
97:
98: $fr = "\x50\x4b\x03\x04";
99: $fr .= "\x14\x00"; // ver needed to extract
100: $fr .= "\x00\x00"; // gen purpose bit flag
101: $fr .= "\x08\x00"; // compression method
102: $fr .= $hexdtime; // last mod time and date
103: // "local file header" segment
104: $unc_len = strlen($data);
105: $crc = crc32($data);
106: $zdata = gzcompress($data);
107: $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
108: $c_len = strlen($zdata);
109: $fr .= pack('V', $crc); // crc32
110: $fr .= pack('V', $c_len); // compressed filesize
111: $fr .= pack('V', $unc_len); // uncompressed filesize
112: $fr .= pack('v', strlen($name)); // length of filename
113: $fr .= pack('v', 0); // extra field length
114: $fr .= $name;
115: // "file data" segment
116: $fr .= $zdata;
117: // "data descriptor" segment (optional but necessary if archive is not
118: // served as file)
119: $fr .= pack('V', $crc); // crc32
120: $fr .= pack('V', $c_len); // compressed filesize
121: $fr .= pack('V', $unc_len); // uncompressed filesize
122: // add this entry to array
123: $this->datasec[] = $fr;
124: $new_offset = strlen(implode('', $this->datasec));
125: // now add to central directory record
126: $cdrec = "\x50\x4b\x01\x02";
127: $cdrec .= "\x00\x00"; // version made by
128: $cdrec .= "\x14\x00"; // version needed to extract
129: $cdrec .= "\x00\x00"; // gen purpose bit flag
130: $cdrec .= "\x08\x00"; // compression method
131: $cdrec .= $hexdtime; // last mod time & date
132: $cdrec .= pack('V', $crc); // crc32
133: $cdrec .= pack('V', $c_len); // compressed filesize
134: $cdrec .= pack('V', $unc_len); // uncompressed filesize
135: $cdrec .= pack('v', strlen($name)); // length of filename
136: $cdrec .= pack('v', 0); // extra field length
137: $cdrec .= pack('v', 0); // file comment length
138: $cdrec .= pack('v', 0); // disk number start
139: $cdrec .= pack('v', 0); // internal file attributes
140: $cdrec .= pack('V', 32); // external file attributes - 'archive' bit set
141: $cdrec .= pack('V', $this->old_offset); // relative offset of local header
142: $this->old_offset = $new_offset;
143: $cdrec .= $name;
144: // optional extra field, file comment goes here
145: // save to central directory
146: $this->ctrl_dir[] = $cdrec;
147: } // end of the 'addFile()' method
148:
149: /**
150: * Dumps out file
151: *
152: * @return string the zipped file
153: * @access public
154: */
155: public function file()
156: {
157: $data = implode('', $this->datasec);
158: $ctrldir = implode('', $this->ctrl_dir);
159:
160: return $data . $ctrldir . $this->eof_ctrl_dir . pack('v', count($this->ctrl_dir)) . // total # of entries "on this disk"
161: pack('v', count($this->ctrl_dir)) . // total # of entries overall
162: pack('V', strlen($ctrldir)) . // size of central dir
163: pack('V', strlen($data)) . // offset to start of central dir
164: "\x00\x00"; // .zip file comment length
165: } // end of the 'file()' method
166: } // end of the 'zipfile' class