blob: da3e5eb63a0b942648ce51edb656ff08df8b51f3 [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
Barry Mienydd671972010-10-04 16:33:58 +020035 var $zipdata = '';
36 var $directory = '';
37 var $entries = 0;
38 var $file_num = 0;
Derek Allard2067d1a2008-11-13 22:59:24 +000039 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");
Barry Mienydd671972010-10-04 16:33:58 +020045
Greg Aker5ed19b42010-03-19 12:13:14 -050046 $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
Barry Mienydd671972010-10-04 16:33:58 +020075 // --------------------------------------------------------------------
Greg Aker5ed19b42010-03-19 12:13:14 -050076
77 /**
78 * Get file/directory modification time
Barry Mienydd671972010-10-04 16:33:58 +020079 *
Greg Aker5ed19b42010-03-19 12:13:14 -050080 * If this is a newly created file/dir, we will set the time to 'now'
81 *
82 * @param string path to file
Barry Mienydd671972010-10-04 16:33:58 +020083 * @return array filemtime/filemdate
Greg Aker5ed19b42010-03-19 12:13:14 -050084 */
85 function _get_mod_time($dir)
86 {
87 // filemtime() will return false, but it does raise an error.
Barry Mienydd671972010-10-04 16:33:58 +020088 $date = (@filemtime($dir)) ? filemtime($dir) : getdate($this->now);
Greg Aker5ed19b42010-03-19 12:13:14 -050089
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'];
Barry Mienydd671972010-10-04 16:33:58 +020092
Greg Aker5ed19b42010-03-19 12:13:14 -050093 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)
Barry Mienydd671972010-10-04 16:33:58 +0200106 {
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 }
Barry Mienydd671972010-10-04 16:33:58 +0200143
Derek Allard2067d1a2008-11-13 22:59:24 +0000144 // --------------------------------------------------------------------
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
Barry Mienydd671972010-10-04 16:33:58 +0200157 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000158 function add_data($filepath, $data = NULL)
Barry Mienydd671972010-10-04 16:33:58 +0200159 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 if (is_array($filepath))
161 {
162 foreach ($filepath as $path => $data)
163 {
Barry Mienydd671972010-10-04 16:33:58 +0200164 $file_data = $this->_get_mod_time($path);
Greg Aker5ed19b42010-03-19 12:13:14 -0500165
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);
Barry Mienydd671972010-10-04 16:33:58 +0200172
Greg Aker5ed19b42010-03-19 12:13:14 -0500173 $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
Barry Mienydd671972010-10-04 16:33:58 +0200186 */
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 }
Barry Mienydd671972010-10-04 16:33:58 +0200230
Derek Allard2067d1a2008-11-13 22:59:24 +0000231 // --------------------------------------------------------------------
232
233 /**
234 * Read the contents of a file and add it to the zip
235 *
236 * @access public
237 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200238 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 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);
Barry Mienydd671972010-10-04 16:33:58 +0200249
Derek Allard2067d1a2008-11-13 22:59:24 +0000250 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 // ------------------------------------------------------------------------
Barry Mienydd671972010-10-04 16:33:58 +0200262
Derek Allard2067d1a2008-11-13 22:59:24 +0000263 /**
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
Phil Sturgeon26872de2010-05-11 11:41:59 +0100273 */
274 function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)
275 {
Derek Jones2735b3e2010-05-11 08:58:21 -0500276 if ( ! $fp = @opendir($path))
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 {
Phil Sturgeon26872de2010-05-11 11:41:59 +0100278 return FALSE;
279 }
280
281 // Set the original directory root for child dir's to use as relative
282 if ($root_path === NULL)
283 {
284 $root_path = dirname($path).'/';
285 }
286
287 while (FALSE !== ($file = readdir($fp)))
288 {
289 if(substr($file, 0, 1) == '.')
Derek Allard2067d1a2008-11-13 22:59:24 +0000290 {
Phil Sturgeon26872de2010-05-11 11:41:59 +0100291 continue;
292 }
293
294 if (@is_dir($path.$file))
295 {
296 $this->read_dir($path.$file."/", $preserve_filepath, $root_path);
297 }
Phil Sturgeon26872de2010-05-11 11:41:59 +0100298 else
299 {
300 if (FALSE !== ($data = file_get_contents($path.$file)))
Derek Allard2067d1a2008-11-13 22:59:24 +0000301 {
Phil Sturgeon26872de2010-05-11 11:41:59 +0100302 $name = str_replace("\\", "/", $path);
303
304 if ($preserve_filepath === FALSE)
305 {
306 $name = str_replace($root_path, '', $name);
Derek Allard2067d1a2008-11-13 22:59:24 +0000307 }
Phil Sturgeon26872de2010-05-11 11:41:59 +0100308
309 $this->add_data($name.$file, $data);
Derek Allard2067d1a2008-11-13 22:59:24 +0000310 }
311 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000312 }
Derek Jones2735b3e2010-05-11 08:58:21 -0500313
Phil Sturgeon26872de2010-05-11 11:41:59 +0100314 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000315 }
316
317 // --------------------------------------------------------------------
318
319 /**
320 * Get the Zip file
321 *
322 * @access public
323 * @return binary string
Barry Mienydd671972010-10-04 16:33:58 +0200324 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000325 function get_zip()
326 {
327 // Is there any data to return?
328 if ($this->entries == 0)
329 {
330 return FALSE;
331 }
332
333 $zip_data = $this->zipdata;
334 $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00";
335 $zip_data .= pack('v', $this->entries); // total # of entries "on this disk"
336 $zip_data .= pack('v', $this->entries); // total # of entries overall
337 $zip_data .= pack('V', strlen($this->directory)); // size of central dir
338 $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir
339 $zip_data .= "\x00\x00"; // .zip file comment length
340
341 return $zip_data;
342 }
Barry Mienydd671972010-10-04 16:33:58 +0200343
Derek Allard2067d1a2008-11-13 22:59:24 +0000344 // --------------------------------------------------------------------
345
346 /**
347 * Write File to the specified directory
348 *
349 * Lets you write a file
350 *
351 * @access public
352 * @param string the file name
353 * @return bool
Barry Mienydd671972010-10-04 16:33:58 +0200354 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000355 function archive($filepath)
356 {
357 if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE)))
358 {
359 return FALSE;
360 }
361
Barry Mienydd671972010-10-04 16:33:58 +0200362 flock($fp, LOCK_EX);
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 fwrite($fp, $this->get_zip());
364 flock($fp, LOCK_UN);
365 fclose($fp);
366
Barry Mienydd671972010-10-04 16:33:58 +0200367 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000368 }
369
370 // --------------------------------------------------------------------
371
372 /**
373 * Download
374 *
375 * @access public
376 * @param string the file name
377 * @param string the data to be encoded
378 * @return bool
379 */
380 function download($filename = 'backup.zip')
381 {
382 if ( ! preg_match("|.+?\.zip$|", $filename))
383 {
384 $filename .= '.zip';
385 }
386
Derek Allard2067d1a2008-11-13 22:59:24 +0000387 $CI =& get_instance();
388 $CI->load->helper('download');
389
Derek Allard8dc2c7c2009-12-07 16:07:15 +0000390 $get_zip = $this->get_zip();
391
392 $zip_content =& $get_zip;
393
Derek Allard2067d1a2008-11-13 22:59:24 +0000394 force_download($filename, $zip_content);
395 }
396
397 // --------------------------------------------------------------------
398
399 /**
400 * Initialize Data
401 *
402 * Lets you clear current zip data. Useful if you need to create
403 * multiple zips with different data.
404 *
405 * @access public
406 * @return void
Barry Mienydd671972010-10-04 16:33:58 +0200407 */
Derek Allard2067d1a2008-11-13 22:59:24 +0000408 function clear_data()
409 {
410 $this->zipdata = '';
411 $this->directory = '';
412 $this->entries = 0;
413 $this->file_num = 0;
414 $this->offset = 0;
415 }
Barry Mienydd671972010-10-04 16:33:58 +0200416
Derek Allard2067d1a2008-11-13 22:59:24 +0000417}
418
419/* End of file Zip.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000420/* Location: ./system/libraries/Zip.php */