Take two:
[www-register-wizard.git] / libraries / Zip.php
1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');\r
2 /**\r
3  * CodeIgniter\r
4  *\r
5  * An open source application development framework for PHP 4.3.2 or newer\r
6  *\r
7  * @package             CodeIgniter\r
8  * @author              ExpressionEngine Dev Team\r
9  * @copyright   Copyright (c) 2008, EllisLab, Inc.\r
10  * @license             http://codeigniter.com/user_guide/license.html\r
11  * @link                http://codeigniter.com\r
12  * @since               Version 1.0\r
13  * @filesource\r
14  */\r
15 \r
16 // ------------------------------------------------------------------------\r
17 \r
18 /**\r
19  * Zip Compression Class\r
20  *\r
21  * This class is based on a library I found at Zend:\r
22  * http://www.zend.com/codex.php?id=696&single=1\r
23  *\r
24  * The original library is a little rough around the edges so I\r
25  * refactored it and added several additional methods -- Rick Ellis\r
26  *\r
27  * @package             CodeIgniter\r
28  * @subpackage  Libraries\r
29  * @category    Encryption\r
30  * @author              ExpressionEngine Dev Team\r
31  * @link                http://codeigniter.com/user_guide/libraries/zip.html\r
32  */\r
33 class CI_Zip  {\r
34 \r
35         var $zipdata    = '';\r
36         var $directory  = '';\r
37         var $entries    = 0;\r
38         var $file_num   = 0;\r
39         var $offset             = 0;\r
40 \r
41         function CI_Zip()\r
42         {\r
43                 log_message('debug', "Zip Compression Class Initialized");\r
44         }\r
45 \r
46         // --------------------------------------------------------------------\r
47 \r
48         /**\r
49          * Add Directory\r
50          *\r
51          * Lets you add a virtual directory into which you can place files.\r
52          *\r
53          * @access      public\r
54          * @param       mixed   the directory name. Can be string or array\r
55          * @return      void\r
56          */\r
57         function add_dir($directory)\r
58         {\r
59                 foreach ((array)$directory as $dir)\r
60                 {\r
61                         if ( ! preg_match("|.+/$|", $dir))\r
62                         {\r
63                                 $dir .= '/';\r
64                         }\r
65 \r
66                         $this->_add_dir($dir);\r
67                 }\r
68         }\r
69 \r
70         // --------------------------------------------------------------------\r
71 \r
72         /**\r
73          * Add Directory\r
74          *\r
75          * @access      private\r
76          * @param       string  the directory name\r
77          * @return      void\r
78          */\r
79         function _add_dir($dir)\r
80         {\r
81                 $dir = str_replace("\\", "/", $dir);\r
82 \r
83                 $this->zipdata .=\r
84                         "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"\r
85                         .pack('V', 0) // crc32\r
86                         .pack('V', 0) // compressed filesize\r
87                         .pack('V', 0) // uncompressed filesize\r
88                         .pack('v', strlen($dir)) // length of pathname\r
89                         .pack('v', 0) // extra field length\r
90                         .$dir\r
91                         // below is "data descriptor" segment\r
92                         .pack('V', 0) // crc32\r
93                         .pack('V', 0) // compressed filesize\r
94                         .pack('V', 0); // uncompressed filesize\r
95 \r
96                 $this->directory .=\r
97                         "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"\r
98                         .pack('V',0) // crc32\r
99                         .pack('V',0) // compressed filesize\r
100                         .pack('V',0) // uncompressed filesize\r
101                         .pack('v', strlen($dir)) // length of pathname\r
102                         .pack('v', 0) // extra field length\r
103                         .pack('v', 0) // file comment length\r
104                         .pack('v', 0) // disk number start\r
105                         .pack('v', 0) // internal file attributes\r
106                         .pack('V', 16) // external file attributes - 'directory' bit set\r
107                         .pack('V', $this->offset) // relative offset of local header\r
108                         .$dir;\r
109 \r
110                 $this->offset = strlen($this->zipdata);\r
111                 $this->entries++;\r
112         }\r
113         \r
114         // --------------------------------------------------------------------\r
115 \r
116         /**\r
117          * Add Data to Zip\r
118          *\r
119          * Lets you add files to the archive. If the path is included\r
120          * in the filename it will be placed within a directory.  Make\r
121          * sure you use add_dir() first to create the folder.\r
122          *\r
123          * @access      public\r
124          * @param       mixed\r
125          * @param       string\r
126          * @return      void\r
127          */     \r
128         function add_data($filepath, $data = NULL)\r
129         {\r
130                 if (is_array($filepath))\r
131                 {\r
132                         foreach ($filepath as $path => $data)\r
133                         {\r
134                                 $this->_add_data($path, $data);\r
135                         }\r
136                 }\r
137                 else\r
138                 {\r
139                         $this->_add_data($filepath, $data);\r
140                 }\r
141         }\r
142 \r
143         // --------------------------------------------------------------------\r
144 \r
145         /**\r
146          * Add Data to Zip\r
147          *\r
148          * @access      private\r
149          * @param       string  the file name/path\r
150          * @param       string  the data to be encoded\r
151          * @return      void\r
152          */     \r
153         function _add_data($filepath, $data)\r
154         {\r
155                 $filepath = str_replace("\\", "/", $filepath);\r
156 \r
157                 $uncompressed_size = strlen($data);\r
158                 $crc32  = crc32($data);\r
159 \r
160                 $gzdata = gzcompress($data);\r
161                 $gzdata = substr($gzdata, 2, -4);\r
162                 $compressed_size = strlen($gzdata);\r
163 \r
164                 $this->zipdata .=\r
165                         "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"\r
166                         .pack('V', $crc32)\r
167                         .pack('V', $compressed_size)\r
168                         .pack('V', $uncompressed_size)\r
169                         .pack('v', strlen($filepath)) // length of filename\r
170                         .pack('v', 0) // extra field length\r
171                         .$filepath\r
172                         .$gzdata; // "file data" segment\r
173 \r
174                 $this->directory .=\r
175                         "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"\r
176                         .pack('V', $crc32)\r
177                         .pack('V', $compressed_size)\r
178                         .pack('V', $uncompressed_size)\r
179                         .pack('v', strlen($filepath)) // length of filename\r
180                         .pack('v', 0) // extra field length\r
181                         .pack('v', 0) // file comment length\r
182                         .pack('v', 0) // disk number start\r
183                         .pack('v', 0) // internal file attributes\r
184                         .pack('V', 32) // external file attributes - 'archive' bit set\r
185                         .pack('V', $this->offset) // relative offset of local header\r
186                         .$filepath;\r
187 \r
188                 $this->offset = strlen($this->zipdata);\r
189                 $this->entries++;\r
190                 $this->file_num++;\r
191         }\r
192         \r
193         // --------------------------------------------------------------------\r
194 \r
195         /**\r
196          * Read the contents of a file and add it to the zip\r
197          *\r
198          * @access      public\r
199          * @return      bool\r
200          */     \r
201         function read_file($path, $preserve_filepath = FALSE)\r
202         {\r
203                 if ( ! file_exists($path))\r
204                 {\r
205                         return FALSE;\r
206                 }\r
207 \r
208                 if (FALSE !== ($data = file_get_contents($path)))\r
209                 {\r
210                         $name = str_replace("\\", "/", $path);\r
211                         \r
212                         if ($preserve_filepath === FALSE)\r
213                         {\r
214                                 $name = preg_replace("|.*/(.+)|", "\\1", $name);\r
215                         }\r
216 \r
217                         $this->add_data($name, $data);\r
218                         return TRUE;\r
219                 }\r
220                 return FALSE;\r
221         }\r
222 \r
223         // ------------------------------------------------------------------------\r
224         \r
225         /**\r
226          * Read a directory and add it to the zip.\r
227          *\r
228          * This function recursively reads a folder and everything it contains (including\r
229          * sub-folders) and creates a zip based on it.  Whatever directory structure\r
230          * is in the original file path will be recreated in the zip file.\r
231          *\r
232          * @access      public\r
233          * @param       string  path to source\r
234          * @return      bool\r
235          */     \r
236         function read_dir($path)\r
237         {       \r
238                 if ($fp = @opendir($path))\r
239                 {\r
240                         while (FALSE !== ($file = readdir($fp)))\r
241                         {\r
242                                 if (@is_dir($path.$file) && substr($file, 0, 1) != '.')\r
243                                 {                                       \r
244                                         $this->read_dir($path.$file."/");\r
245                                 }\r
246                                 elseif (substr($file, 0, 1) != ".")\r
247                                 {\r
248                                         if (FALSE !== ($data = file_get_contents($path.$file)))\r
249                                         {                                               \r
250                                                 $this->add_data(str_replace("\\", "/", $path).$file, $data);\r
251                                         }\r
252                                 }\r
253                         }\r
254                         return TRUE;\r
255                 }\r
256         }\r
257 \r
258         // --------------------------------------------------------------------\r
259 \r
260         /**\r
261          * Get the Zip file\r
262          *\r
263          * @access      public\r
264          * @return      binary string\r
265          */     \r
266         function get_zip()\r
267         {\r
268                 // Is there any data to return?\r
269                 if ($this->entries == 0)\r
270                 {\r
271                         return FALSE;\r
272                 }\r
273 \r
274                 $zip_data = $this->zipdata;\r
275                 $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";\r
276                 $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"\r
277                 $zip_data .= pack('v', $this->entries); // total # of entries overall\r
278                 $zip_data .= pack('V', strlen($this->directory)); // size of central dir\r
279                 $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir\r
280                 $zip_data .= "\x00\x00"; // .zip file comment length\r
281 \r
282                 return $zip_data;\r
283         }\r
284         \r
285         // --------------------------------------------------------------------\r
286 \r
287         /**\r
288          * Write File to the specified directory\r
289          *\r
290          * Lets you write a file\r
291          *\r
292          * @access      public\r
293          * @param       string  the file name\r
294          * @return      bool\r
295          */     \r
296         function archive($filepath)\r
297         {\r
298                 if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)))\r
299                 {\r
300                         return FALSE;\r
301                 }\r
302 \r
303                 flock($fp, LOCK_EX);    \r
304                 fwrite($fp, $this->get_zip());\r
305                 flock($fp, LOCK_UN);\r
306                 fclose($fp);\r
307 \r
308                 return TRUE;    \r
309         }\r
310 \r
311         // --------------------------------------------------------------------\r
312 \r
313         /**\r
314          * Download\r
315          *\r
316          * @access      public\r
317          * @param       string  the file name\r
318          * @param       string  the data to be encoded\r
319          * @return      bool\r
320          */\r
321         function download($filename = 'backup.zip')\r
322         {\r
323                 if ( ! preg_match("|.+?\.zip$|", $filename))\r
324                 {\r
325                         $filename .= '.zip';\r
326                 }\r
327 \r
328                 $zip_content =& $this->get_zip();\r
329 \r
330                 $CI =& get_instance();\r
331                 $CI->load->helper('download');\r
332 \r
333                 force_download($filename, $zip_content);\r
334         }\r
335 \r
336         // --------------------------------------------------------------------\r
337 \r
338         /**\r
339          * Initialize Data\r
340          *\r
341          * Lets you clear current zip data.  Useful if you need to create\r
342          * multiple zips with different data.\r
343          *\r
344          * @access      public\r
345          * @return      void\r
346          */             \r
347         function clear_data()\r
348         {\r
349                 $this->zipdata          = '';\r
350                 $this->directory        = '';\r
351                 $this->entries          = 0;\r
352                 $this->file_num         = 0;\r
353                 $this->offset           = 0;\r
354         }\r
355         \r
356 }\r
357 \r
358 /* End of file Zip.php */\r
359 /* Location: ./system/libraries/Zip.php */