]>
Commit | Line | Data |
---|---|---|
59c06b17 CS |
1 | <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); |
2 | /** | |
3 | * CodeIgniter | |
4 | * | |
5 | * An open source application development framework for PHP 5.1.6 or newer | |
6 | * | |
7 | * @package CodeIgniter | |
8 | * @author ExpressionEngine Dev Team | |
9 | * @copyright Copyright (c) 2008 - 2011, 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 | var $now; | |
41 | ||
42 | /** | |
43 | * Constructor | |
44 | */ | |
45 | public function __construct() | |
46 | { | |
47 | log_message('debug', "Zip Compression Class Initialized"); | |
48 | ||
49 | $this->now = time(); | |
50 | } | |
51 | ||
52 | // -------------------------------------------------------------------- | |
53 | ||
54 | /** | |
55 | * Add Directory | |
56 | * | |
57 | * Lets you add a virtual directory into which you can place files. | |
58 | * | |
59 | * @access public | |
60 | * @param mixed the directory name. Can be string or array | |
61 | * @return void | |
62 | */ | |
63 | function add_dir($directory) | |
64 | { | |
65 | foreach ((array)$directory as $dir) | |
66 | { | |
67 | if ( ! preg_match("|.+/$|", $dir)) | |
68 | { | |
69 | $dir .= '/'; | |
70 | } | |
71 | ||
72 | $dir_time = $this->_get_mod_time($dir); | |
73 | ||
74 | $this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']); | |
75 | } | |
76 | } | |
77 | ||
78 | // -------------------------------------------------------------------- | |
79 | ||
80 | /** | |
81 | * Get file/directory modification time | |
82 | * | |
83 | * If this is a newly created file/dir, we will set the time to 'now' | |
84 | * | |
85 | * @param string path to file | |
86 | * @return array filemtime/filemdate | |
87 | */ | |
88 | function _get_mod_time($dir) | |
89 | { | |
90 | // filemtime() will return false, but it does raise an error. | |
91 | $date = (@filemtime($dir)) ? filemtime($dir) : getdate($this->now); | |
92 | ||
93 | $time['file_mtime'] = ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2; | |
94 | $time['file_mdate'] = (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday']; | |
95 | ||
96 | return $time; | |
97 | } | |
98 | ||
99 | // -------------------------------------------------------------------- | |
100 | ||
101 | /** | |
102 | * Add Directory | |
103 | * | |
104 | * @access private | |
105 | * @param string the directory name | |
106 | * @return void | |
107 | */ | |
108 | function _add_dir($dir, $file_mtime, $file_mdate) | |
109 | { | |
110 | $dir = str_replace("\\", "/", $dir); | |
111 | ||
112 | $this->zipdata .= | |
113 | "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" | |
114 | .pack('v', $file_mtime) | |
115 | .pack('v', $file_mdate) | |
116 | .pack('V', 0) // crc32 | |
117 | .pack('V', 0) // compressed filesize | |
118 | .pack('V', 0) // uncompressed filesize | |
119 | .pack('v', strlen($dir)) // length of pathname | |
120 | .pack('v', 0) // extra field length | |
121 | .$dir | |
122 | // below is "data descriptor" segment | |
123 | .pack('V', 0) // crc32 | |
124 | .pack('V', 0) // compressed filesize | |
125 | .pack('V', 0); // uncompressed filesize | |
126 | ||
127 | $this->directory .= | |
128 | "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00" | |
129 | .pack('v', $file_mtime) | |
130 | .pack('v', $file_mdate) | |
131 | .pack('V',0) // crc32 | |
132 | .pack('V',0) // compressed filesize | |
133 | .pack('V',0) // uncompressed filesize | |
134 | .pack('v', strlen($dir)) // length of pathname | |
135 | .pack('v', 0) // extra field length | |
136 | .pack('v', 0) // file comment length | |
137 | .pack('v', 0) // disk number start | |
138 | .pack('v', 0) // internal file attributes | |
139 | .pack('V', 16) // external file attributes - 'directory' bit set | |
140 | .pack('V', $this->offset) // relative offset of local header | |
141 | .$dir; | |
142 | ||
143 | $this->offset = strlen($this->zipdata); | |
144 | $this->entries++; | |
145 | } | |
146 | ||
147 | // -------------------------------------------------------------------- | |
148 | ||
149 | /** | |
150 | * Add Data to Zip | |
151 | * | |
152 | * Lets you add files to the archive. If the path is included | |
153 | * in the filename it will be placed within a directory. Make | |
154 | * sure you use add_dir() first to create the folder. | |
155 | * | |
156 | * @access public | |
157 | * @param mixed | |
158 | * @param string | |
159 | * @return void | |
160 | */ | |
161 | function add_data($filepath, $data = NULL) | |
162 | { | |
163 | if (is_array($filepath)) | |
164 | { | |
165 | foreach ($filepath as $path => $data) | |
166 | { | |
167 | $file_data = $this->_get_mod_time($path); | |
168 | ||
169 | $this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']); | |
170 | } | |
171 | } | |
172 | else | |
173 | { | |
174 | $file_data = $this->_get_mod_time($filepath); | |
175 | ||
176 | $this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']); | |
177 | } | |
178 | } | |
179 | ||
180 | // -------------------------------------------------------------------- | |
181 | ||
182 | /** | |
183 | * Add Data to Zip | |
184 | * | |
185 | * @access private | |
186 | * @param string the file name/path | |
187 | * @param string the data to be encoded | |
188 | * @return void | |
189 | */ | |
190 | function _add_data($filepath, $data, $file_mtime, $file_mdate) | |
191 | { | |
192 | $filepath = str_replace("\\", "/", $filepath); | |
193 | ||
194 | $uncompressed_size = strlen($data); | |
195 | $crc32 = crc32($data); | |
196 | ||
197 | $gzdata = gzcompress($data); | |
198 | $gzdata = substr($gzdata, 2, -4); | |
199 | $compressed_size = strlen($gzdata); | |
200 | ||
201 | $this->zipdata .= | |
202 | "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00" | |
203 | .pack('v', $file_mtime) | |
204 | .pack('v', $file_mdate) | |
205 | .pack('V', $crc32) | |
206 | .pack('V', $compressed_size) | |
207 | .pack('V', $uncompressed_size) | |
208 | .pack('v', strlen($filepath)) // length of filename | |
209 | .pack('v', 0) // extra field length | |
210 | .$filepath | |
211 | .$gzdata; // "file data" segment | |
212 | ||
213 | $this->directory .= | |
214 | "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00" | |
215 | .pack('v', $file_mtime) | |
216 | .pack('v', $file_mdate) | |
217 | .pack('V', $crc32) | |
218 | .pack('V', $compressed_size) | |
219 | .pack('V', $uncompressed_size) | |
220 | .pack('v', strlen($filepath)) // length of filename | |
221 | .pack('v', 0) // extra field length | |
222 | .pack('v', 0) // file comment length | |
223 | .pack('v', 0) // disk number start | |
224 | .pack('v', 0) // internal file attributes | |
225 | .pack('V', 32) // external file attributes - 'archive' bit set | |
226 | .pack('V', $this->offset) // relative offset of local header | |
227 | .$filepath; | |
228 | ||
229 | $this->offset = strlen($this->zipdata); | |
230 | $this->entries++; | |
231 | $this->file_num++; | |
232 | } | |
233 | ||
234 | // -------------------------------------------------------------------- | |
235 | ||
236 | /** | |
237 | * Read the contents of a file and add it to the zip | |
238 | * | |
239 | * @access public | |
240 | * @return bool | |
241 | */ | |
242 | function read_file($path, $preserve_filepath = FALSE) | |
243 | { | |
244 | if ( ! file_exists($path)) | |
245 | { | |
246 | return FALSE; | |
247 | } | |
248 | ||
249 | if (FALSE !== ($data = file_get_contents($path))) | |
250 | { | |
251 | $name = str_replace("\\", "/", $path); | |
252 | ||
253 | if ($preserve_filepath === FALSE) | |
254 | { | |
255 | $name = preg_replace("|.*/(.+)|", "\\1", $name); | |
256 | } | |
257 | ||
258 | $this->add_data($name, $data); | |
259 | return TRUE; | |
260 | } | |
261 | return FALSE; | |
262 | } | |
263 | ||
264 | // ------------------------------------------------------------------------ | |
265 | ||
266 | /** | |
267 | * Read a directory and add it to the zip. | |
268 | * | |
269 | * This function recursively reads a folder and everything it contains (including | |
270 | * sub-folders) and creates a zip based on it. Whatever directory structure | |
271 | * is in the original file path will be recreated in the zip file. | |
272 | * | |
273 | * @access public | |
274 | * @param string path to source | |
275 | * @return bool | |
276 | */ | |
277 | function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL) | |
278 | { | |
279 | if ( ! $fp = @opendir($path)) | |
280 | { | |
281 | return FALSE; | |
282 | } | |
283 | ||
284 | // Set the original directory root for child dir's to use as relative | |
285 | if ($root_path === NULL) | |
286 | { | |
287 | $root_path = dirname($path).'/'; | |
288 | } | |
289 | ||
290 | while (FALSE !== ($file = readdir($fp))) | |
291 | { | |
292 | if (substr($file, 0, 1) == '.') | |
293 | { | |
294 | continue; | |
295 | } | |
296 | ||
297 | if (@is_dir($path.$file)) | |
298 | { | |
299 | $this->read_dir($path.$file."/", $preserve_filepath, $root_path); | |
300 | } | |
301 | else | |
302 | { | |
303 | if (FALSE !== ($data = file_get_contents($path.$file))) | |
304 | { | |
305 | $name = str_replace("\\", "/", $path); | |
306 | ||
307 | if ($preserve_filepath === FALSE) | |
308 | { | |
309 | $name = str_replace($root_path, '', $name); | |
310 | } | |
311 | ||
312 | $this->add_data($name.$file, $data); | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | return TRUE; | |
318 | } | |
319 | ||
320 | // -------------------------------------------------------------------- | |
321 | ||
322 | /** | |
323 | * Get the Zip file | |
324 | * | |
325 | * @access public | |
326 | * @return binary string | |
327 | */ | |
328 | function get_zip() | |
329 | { | |
330 | // Is there any data to return? | |
331 | if ($this->entries == 0) | |
332 | { | |
333 | return FALSE; | |
334 | } | |
335 | ||
336 | $zip_data = $this->zipdata; | |
337 | $zip_data .= $this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"; | |
338 | $zip_data .= pack('v', $this->entries); // total # of entries "on this disk" | |
339 | $zip_data .= pack('v', $this->entries); // total # of entries overall | |
340 | $zip_data .= pack('V', strlen($this->directory)); // size of central dir | |
341 | $zip_data .= pack('V', strlen($this->zipdata)); // offset to start of central dir | |
342 | $zip_data .= "\x00\x00"; // .zip file comment length | |
343 | ||
344 | return $zip_data; | |
345 | } | |
346 | ||
347 | // -------------------------------------------------------------------- | |
348 | ||
349 | /** | |
350 | * Write File to the specified directory | |
351 | * | |
352 | * Lets you write a file | |
353 | * | |
354 | * @access public | |
355 | * @param string the file name | |
356 | * @return bool | |
357 | */ | |
358 | function archive($filepath) | |
359 | { | |
360 | if ( ! ($fp = @fopen($filepath, FOPEN_WRITE_CREATE_DESTRUCTIVE))) | |
361 | { | |
362 | return FALSE; | |
363 | } | |
364 | ||
365 | flock($fp, LOCK_EX); | |
366 | fwrite($fp, $this->get_zip()); | |
367 | flock($fp, LOCK_UN); | |
368 | fclose($fp); | |
369 | ||
370 | return TRUE; | |
371 | } | |
372 | ||
373 | // -------------------------------------------------------------------- | |
374 | ||
375 | /** | |
376 | * Download | |
377 | * | |
378 | * @access public | |
379 | * @param string the file name | |
380 | * @param string the data to be encoded | |
381 | * @return bool | |
382 | */ | |
383 | function download($filename = 'backup.zip') | |
384 | { | |
385 | if ( ! preg_match("|.+?\.zip$|", $filename)) | |
386 | { | |
387 | $filename .= '.zip'; | |
388 | } | |
389 | ||
390 | $CI =& get_instance(); | |
391 | $CI->load->helper('download'); | |
392 | ||
393 | $get_zip = $this->get_zip(); | |
394 | ||
395 | $zip_content =& $get_zip; | |
396 | ||
397 | force_download($filename, $zip_content); | |
398 | } | |
399 | ||
400 | // -------------------------------------------------------------------- | |
401 | ||
402 | /** | |
403 | * Initialize Data | |
404 | * | |
405 | * Lets you clear current zip data. Useful if you need to create | |
406 | * multiple zips with different data. | |
407 | * | |
408 | * @access public | |
409 | * @return void | |
410 | */ | |
411 | function clear_data() | |
412 | { | |
413 | $this->zipdata = ''; | |
414 | $this->directory = ''; | |
415 | $this->entries = 0; | |
416 | $this->file_num = 0; | |
417 | $this->offset = 0; | |
418 | } | |
419 | ||
420 | } | |
421 | ||
422 | /* End of file Zip.php */ | |
423 | /* Location: ./system/libraries/Zip.php */ |