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