blob: 4ed1f1820e42d4f3e09cd820c1c7acc9b38248a5 [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 Jonesfc395a12009-04-22 14:15:09 +00009 * @copyright Copyright (c) 2008 - 2009, 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;
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
Derek Allard2067d1a2008-11-13 22:59:24 +0000328 $CI =& get_instance();
329 $CI->load->helper('download');
330
Derek Allard8dc2c7c2009-12-07 16:07:15 +0000331 $get_zip = $this->get_zip();
332
333 $zip_content =& $get_zip;
334
Derek Allard2067d1a2008-11-13 22:59:24 +0000335 force_download($filename, $zip_content);
336 }
337
338 // --------------------------------------------------------------------
339
340 /**
341 * Initialize Data
342 *
343 * Lets you clear current zip data. Useful if you need to create
344 * multiple zips with different data.
345 *
346 * @access public
347 * @return void
348 */
349 function clear_data()
350 {
351 $this->zipdata = '';
352 $this->directory = '';
353 $this->entries = 0;
354 $this->file_num = 0;
355 $this->offset = 0;
356 }
357
358}
359
360/* End of file Zip.php */
Derek Jonesa3ffbbb2008-05-11 18:18:29 +0000361/* Location: ./system/libraries/Zip.php */