blob: 2c4bd255dfaaddaf70b5908516718b60e7572595 [file] [log] [blame]
Derek Allard2067d1a2008-11-13 22:59:24 +00001<?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
Derek Jones7f3719f2010-01-05 13:35:37 +00009 * @copyright Copyright (c) 2008 - 2010, EllisLab, Inc.
Derek Allard2067d1a2008-11-13 22:59:24 +000010 * @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 */
33class CI_Zip {
34
35 var $zipdata = '';
36 var $directory = '';
37 var $entries = 0;
38 var $file_num = 0;
39 var $offset = 0;
Greg Aker5ed19b42010-03-19 12:13:14 -050040 var $now;
Derek Allard2067d1a2008-11-13 22:59:24 +000041
42 function CI_Zip()
43 {
44 log_message('debug', "Zip Compression Class Initialized");
Greg Aker5ed19b42010-03-19 12:13:14 -050045
46 $this->now = time();
Derek Allard2067d1a2008-11-13 22:59:24 +000047 }
48
49 // --------------------------------------------------------------------
50
51 /**
52 * Add Directory
53 *
54 * Lets you add a virtual directory into which you can place files.
55 *
56 * @access public
57 * @param mixed the directory name. Can be string or array
58 * @return void
59 */
60 function add_dir($directory)
61 {
62 foreach ((array)$directory as $dir)
63 {
64 if ( ! preg_match("|.+/$|", $dir))
65 {
66 $dir .= '/';
67 }
68
Greg Aker5ed19b42010-03-19 12:13:14 -050069 $dir_time = $this->_get_mod_time($dir);
70
71 $this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);
Derek Allard2067d1a2008-11-13 22:59:24 +000072 }
73 }
74
Greg Aker5ed19b42010-03-19 12:13:14 -050075 // --------------------------------------------------------------------
76
77 /**
78 * Get file/directory modification time
79 *
80 * If this is a newly created file/dir, we will set the time to 'now'
81 *
82 * @param string path to file
83 * @return array filemtime/filemdate
84 */
85 function _get_mod_time($dir)
86 {
87 // filemtime() will return false, but it does raise an error.
88 $date = (@filemtime($dir)) ? filemtime($dir) : getdate($this->now);
89
90 $time['file_mtime'] = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2;
91 $time['file_mdate'] = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday'];
92
93 return $time;
94 }
95
Derek Allard2067d1a2008-11-13 22:59:24 +000096 // --------------------------------------------------------------------
97
98 /**
99 * Add Directory
100 *
101 * @access private
102 * @param string the directory name
103 * @return void
104 */
Greg Aker5ed19b42010-03-19 12:13:14 -0500105 function _add_dir($dir, $file_mtime, $file_mdate)
106 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000107 $dir = str_replace("\\", "/", $dir);
108
109 $this->zipdata .=
Greg Aker5ed19b42010-03-19 12:13:14 -0500110 "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00"
111 .pack('v', $file_mtime)
112 .pack('v', $file_mdate)
Derek Allard2067d1a2008-11-13 22:59:24 +0000113 .pack('V', 0) // crc32
114 .pack('V', 0) // compressed filesize
115 .pack('V', 0) // uncompressed filesize
116 .pack('v', strlen($dir)) // length of pathname
117 .pack('v', 0) // extra field length
118 .$dir
119 // below is "data descriptor" segment
120 .pack('V', 0) // crc32
121 .pack('V', 0) // compressed filesize
122 .pack('V', 0); // uncompressed filesize
123
124 $this->directory .=
Greg Aker5ed19b42010-03-19 12:13:14 -0500125 "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00"
126 .pack('v', $file_mtime)
127 .pack('v', $file_mdate)
Derek Allard2067d1a2008-11-13 22:59:24 +0000128 .pack('V',0) // crc32
129 .pack('V',0) // compressed filesize
130 .pack('V',0) // uncompressed filesize
131 .pack('v', strlen($dir)) // length of pathname
132 .pack('v', 0) // extra field length
133 .pack('v', 0) // file comment length
134 .pack('v', 0) // disk number start
135 .pack('v', 0) // internal file attributes
136 .pack('V', 16) // external file attributes - 'directory' bit set
137 .pack('V', $this->offset) // relative offset of local header
138 .$dir;
139
140 $this->offset = strlen($this->zipdata);
141 $this->entries++;
142 }
143
144 // --------------------------------------------------------------------
145
146 /**
147 * Add Data to Zip
148 *
149 * Lets you add files to the archive. If the path is included
150 * in the filename it will be placed within a directory. Make
151 * sure you use add_dir() first to create the folder.
152 *
153 * @access public
154 * @param mixed
155 * @param string
156 * @return void
157 */
158 function add_data($filepath, $data = NULL)
Greg Aker5ed19b42010-03-19 12:13:14 -0500159 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 if (is_array($filepath))
161 {
162 foreach ($filepath as $path => $data)
163 {
Greg Aker5ed19b42010-03-19 12:13:14 -0500164 $file_data = $this->_get_mod_time($path);
165
166 $this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);
Derek Allard2067d1a2008-11-13 22:59:24 +0000167 }
168 }
169 else
170 {
Greg Aker5ed19b42010-03-19 12:13:14 -0500171 $file_data = $this->_get_mod_time($filepath);
172
173 $this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);
Derek Allard2067d1a2008-11-13 22:59:24 +0000174 }
175 }
176
177 // --------------------------------------------------------------------
178
179 /**
180 * Add Data to Zip
181 *
182 * @access private
183 * @param string the file name/path
184 * @param string the data to be encoded
185 * @return void
186 */
Greg Aker5ed19b42010-03-19 12:13:14 -0500187 function _add_data($filepath, $data, $file_mtime, $file_mdate)
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 {
189 $filepath = str_replace("\\", "/", $filepath);
190
191 $uncompressed_size = strlen($data);
192 $crc32 = crc32($data);
193
194 $gzdata = gzcompress($data);
195 $gzdata = substr($gzdata, 2, -4);
196 $compressed_size = strlen($gzdata);
197
198 $this->zipdata .=
Greg Aker5ed19b42010-03-19 12:13:14 -0500199 "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00"
200 .pack('v', $file_mtime)
201 .pack('v', $file_mdate)
Derek Allard2067d1a2008-11-13 22:59:24 +0000202 .pack('V', $crc32)
203 .pack('V', $compressed_size)
204 .pack('V', $uncompressed_size)
205 .pack('v', strlen($filepath)) // length of filename
206 .pack('v', 0) // extra field length
207 .$filepath
208 .$gzdata; // "file data" segment
209
210 $this->directory .=
Greg Aker5ed19b42010-03-19 12:13:14 -0500211 "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00"
212 .pack('v', $file_mtime)
213 .pack('v', $file_mdate)
Derek Allard2067d1a2008-11-13 22:59:24 +0000214 .pack('V', $crc32)
215 .pack('V', $compressed_size)
216 .pack('V', $uncompressed_size)
217 .pack('v', strlen($filepath)) // length of filename
218 .pack('v', 0) // extra field length
219 .pack('v', 0) // file comment length
220 .pack('v', 0) // disk number start
221 .pack('v', 0) // internal file attributes
222 .pack('V', 32) // external file attributes - 'archive' bit set
223 .pack('V', $this->offset) // relative offset of local header
224 .$filepath;
225
226 $this->offset = strlen($this->zipdata);
227 $this->entries++;
228 $this->file_num++;
229 }
230
231 // --------------------------------------------------------------------
232
233 /**
234 * Read the contents of a file and add it to the zip
235 *
236 * @access public
237 * @return bool
238 */
239 function read_file($path, $preserve_filepath = FALSE)
240 {
241 if ( ! file_exists($path))
242 {
243 return FALSE;
244 }
245
246 if (FALSE !== ($data = file_get_contents($path)))
247 {
248 $name = str_replace("\\", "/", $path);
249
250 if ($preserve_filepath === FALSE)
251 {
252 $name = preg_replace("|.*/(.+)|", "\\1", $name);
253 }
254
255 $this->add_data($name, $data);
256 return TRUE;
257 }
258 return FALSE;
259 }
260
261 // ------------------------------------------------------------------------
262
263 /**
264 * Read a directory and add it to the zip.
265 *
266 * This function recursively reads a folder and everything it contains (including
267 * sub-folders) and creates a zip based on it. Whatever directory structure
268 * is in the original file path will be recreated in the zip file.
269 *
270 * @access public
271 * @param string path to source
272 * @return bool
273 */
274 function read_dir($path)
275 {
276 if ($fp = @opendir($path))
277 {
278 while (FALSE !== ($file = readdir($fp)))
279 {
280 if (@is_dir($path.$file) && substr($file, 0, 1) != '.')
281 {
282 $this->read_dir($path.$file."/");
283 }
284 elseif (substr($file, 0, 1) != ".")
285 {
286 if (FALSE !== ($data = file_get_contents($path.$file)))
287 {
288 $this->add_data(str_replace("\\", "/", $path).$file, $data);
289 }
290 }
291 }
292 return TRUE;
293 }
294 }
295
296 // --------------------------------------------------------------------
297
298 /**
299 * Get the Zip file
300 *
301 * @access public
302 * @return binary string
303 */
304 function get_zip()
305 {
306 // Is there any data to return?
307 if ($this->entries == 0)
308 {
309 return FALSE;
310 }
311
312 $zip_data = $this->zipdata;
313 $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";
314 $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"
315 $zip_data .= pack('v', $this->entries); // total # of entries overall
316 $zip_data .= pack('V', strlen($this->directory)); // size of central dir
317 $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir
318 $zip_data .= "\x00\x00"; // .zip file comment length
319
320 return $zip_data;
321 }
322
323 // --------------------------------------------------------------------
324
325 /**
326 * Write File to the specified directory
327 *
328 * Lets you write a file
329 *
330 * @access public
331 * @param string the file name
332 * @return bool
333 */
334 function archive($filepath)
335 {
336 if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)))
337 {
338 return FALSE;
339 }
340
341 flock($fp, LOCK_EX);
342 fwrite($fp, $this->get_zip());
343 flock($fp, LOCK_UN);
344 fclose($fp);
345
346 return TRUE;
347 }
348
349 // --------------------------------------------------------------------
350
351 /**
352 * Download
353 *
354 * @access public
355 * @param string the file name
356 * @param string the data to be encoded
357 * @return bool
358 */
359 function download($filename = 'backup.zip')
360 {
361 if ( ! preg_match("|.+?\.zip$|", $filename))
362 {
363 $filename .= '.zip';
364 }
365
Derek Allard2067d1a2008-11-13 22:59:24 +0000366 $CI =& get_instance();
367 $CI->load->helper('download');
368
Derek Allard8dc2c7c2009-12-07 16:07:15 +0000369 $get_zip = $this->get_zip();
370
371 $zip_content =& $get_zip;
372
Derek Allard2067d1a2008-11-13 22:59:24 +0000373 force_download($filename, $zip_content);
374 }
375
376 // --------------------------------------------------------------------
377
378 /**
379 * Initialize Data
380 *
381 * Lets you clear current zip data. Useful if you need to create
382 * multiple zips with different data.
383 *
384 * @access public
385 * @return void
386 */
387 function clear_data()
388 {
389 $this->zipdata = '';
390 $this->directory = '';
391 $this->entries = 0;
392 $this->file_num = 0;
393 $this->offset = 0;
394 }
395
396}
397
398/* End of file Zip.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000399/* Location: ./system/libraries/Zip.php */