blob: a3d42f34cb6985fa7c72fbac7c0ba4e9f775320a [file] [log] [blame]
Derek Allardd2df9bc2007-04-15 17:41:17 +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
Derek Allard3d879d52008-01-18 19:41:32 +00008 * @author ExpressionEngine Dev Team
Derek Allardd2df9bc2007-04-15 17:41:17 +00009 * @copyright Copyright (c) 2006, EllisLab, Inc.
Derek Jones7a9193a2008-01-21 18:39:20 +000010 * @license http://codeigniter.com/user_guide/license.html
11 * @link http://codeigniter.com
Derek Allardd2df9bc2007-04-15 17:41:17 +000012 * @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
Derek Allard3d879d52008-01-18 19:41:32 +000030 * @author ExpressionEngine Dev Team
Derek Jones7a9193a2008-01-21 18:39:20 +000031 * @link http://codeigniter.com/user_guide/libraries/zip.html
Derek Allardd2df9bc2007-04-15 17:41:17 +000032 */
33class CI_Zip {
34
35 var $zipfile = '';
36 var $zipdata = array();
37 var $directory = array();
38 var $offset = 0;
39
40 function CI_Zip()
41 {
42 log_message('debug', "Zip Compression Class Initialized");
43 }
44
45 // --------------------------------------------------------------------
46
47 /**
48 * Add Directory
49 *
50 * Lets you add a virtual directory into which you can place files.
51 *
52 * @access public
53 * @param mixed the directory name. Can be string or array
54 * @return void
55 */
56 function add_dir($directory)
57 {
58 foreach ((array)$directory as $dir)
59 {
60 if ( ! preg_match("|.+/$|", $dir))
61 {
62 $dir .= '/';
63 }
64
65 $this->_add_dir($dir);
66 }
67 }
68
69 // --------------------------------------------------------------------
70
71 /**
72 * Add Directory
73 *
74 * @access private
75 * @param string the directory name
76 * @return void
77 */
78 function _add_dir($dir)
79 {
80 $dir = str_replace("\\", "/", $dir);
81
82 $this->zipdata[] = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"
83 .pack('V', 0)
84 .pack('V', 0)
85 .pack('V', 0)
86 .pack('v', strlen($dir))
87 .pack('v', 0)
88 .$dir
89 .pack('V', 0)
90 .pack('V', 0)
91 .pack('V', 0);
92
93 $newoffset = strlen(implode('', $this->zipdata));
94
95 $record = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"
96 .pack('V',0)
97 .pack('V',0)
98 .pack('V',0)
99 .pack('v', strlen($dir))
100 .pack('v', 0)
101 .pack('v', 0)
102 .pack('v', 0)
103 .pack('v', 0)
104 .pack('V', 16)
105 .pack('V', $this->offset)
106 .$dir;
107
108 $this->offset = $newoffset;
109 $this->directory[] = $record;
110 }
111
112 // --------------------------------------------------------------------
113
114 /**
115 * Add Data to Zip
116 *
117 * Lets you add files to the archive. If the path is included
118 * in the filename it will be placed within a directory. Make
119 * sure you use add_dir() first to create the folder.
120 *
121 * @access public
122 * @param mixed
123 * @param string
124 * @return void
125 */
126 function add_data($filepath, $data = NULL)
127 {
128 if (is_array($filepath))
129 {
130 foreach ($filepath as $path => $data)
131 {
132 $this->_add_data($path, $data);
133 }
134 }
135 else
136 {
137 $this->_add_data($filepath, $data);
138 }
139 }
140
141 // --------------------------------------------------------------------
142
143 /**
144 * Add Data to Zip
145 *
146 * @access private
147 * @param string the file name/path
148 * @param string the data to be encoded
149 * @return void
150 */
151 function _add_data($filepath, $data)
152 {
153 $filepath = str_replace("\\", "/", $filepath);
154
155 $oldlen = strlen($data);
156 $crc32 = crc32($data);
157
158 $gzdata = gzcompress($data);
Derek Jones44a736f2007-04-27 17:03:10 +0000159 $gzdata = substr($gzdata, 2, -4);
Derek Allardd2df9bc2007-04-15 17:41:17 +0000160 $newlen = strlen($gzdata);
161
162 $this->zipdata[] = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"
163 .pack('V', $crc32)
164 .pack('V', $newlen)
165 .pack('V', $oldlen)
166 .pack('v', strlen($filepath))
167 .pack('v', 0)
168 .$filepath
Derek Jones44a736f2007-04-27 17:03:10 +0000169 .$gzdata;
Derek Allardd2df9bc2007-04-15 17:41:17 +0000170
171 $newoffset = strlen(implode("", $this->zipdata));
172
173 $record = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00\x00\x00\x00\x00"
174 .pack('V', $crc32)
175 .pack('V', $newlen)
176 .pack('V', $oldlen)
177 .pack('v', strlen($filepath))
178 .pack('v', 0)
179 .pack('v', 0)
180 .pack('v', 0)
181 .pack('v', 0)
182 .pack('V', 32)
183 .pack('V', $this->offset);
184
185 $this->offset = $newoffset;
186 $this->directory[] = $record.$filepath;
187 }
188
189 // --------------------------------------------------------------------
190
191 /**
192 * Read the contents of a file and add it to the zip
193 *
194 * @access public
195 * @return bool
196 */
197 function read_file($path, $preserve_filepath = FALSE)
198 {
199 if ( ! file_exists($path))
200 {
201 return FALSE;
202 }
203
204 if (FALSE !== ($data = file_get_contents($path)))
205 {
206 $name = str_replace("\\", "/", $path);
207
208 if ($preserve_filepath === FALSE)
209 {
210 $name = preg_replace("|.*/(.+)|", "\\1", $name);
211 }
212
213 $this->add_data($name, $data);
214 return TRUE;
215 }
216 return FALSE;
217 }
218
219 // ------------------------------------------------------------------------
220
221 /**
222 * Read a directory and add it to the zip.
223 *
224 * This function recursively reads a folder and everything it contains (including
225 * sub-folders) and creates a zip based on it. Whatever directory structure
226 * is in the original file path will be recreated in the zip file.
227 *
228 * @access public
229 * @param string path to source
230 * @return bool
231 */
232 function read_dir($path)
233 {
234 if ($fp = @opendir($path))
235 {
236 while (FALSE !== ($file = readdir($fp)))
237 {
238 if (@is_dir($path.$file) && substr($file, 0, 1) != '.')
239 {
240 $this->read_dir($path.$file."/");
241 }
242 elseif (substr($file, 0, 1) != ".")
243 {
244 if (FALSE !== ($data = file_get_contents($path.$file)))
245 {
246 $this->add_data(str_replace("\\", "/", $path).$file, $data);
247 }
248 }
249 }
250 return TRUE;
251 }
252 }
253
254 // --------------------------------------------------------------------
255
256 /**
257 * Get the Zip file
258 *
259 * @access public
260 * @return binary string
261 */
262 function get_zip()
263 {
264 // We cache the zip data so multiple calls
265 // do not require recompiling
266 if ($this->zipfile != '')
267 {
268 return $this->zipfile;
269 }
270
271 // Is there any data to return?
272 if (count($this->zipdata) == 0)
273 {
274 return FALSE;
275 }
276
277 $data = implode('', $this->zipdata);
278 $dir = implode('', $this->directory);
279
280 $this->zipfile = $data.$dir."\x50\x4b\x05\x06\x00\x00\x00\x00"
281 .pack('v', sizeof($this->directory))
282 .pack('v', sizeof($this->directory))
283 .pack('V', strlen($dir))
284 .pack('V', strlen($data))
285 ."\x00\x00";
286
287 return $this->zipfile;
288 }
289
290 // --------------------------------------------------------------------
291
292 /**
293 * Write File to the specified directory
294 *
295 * Lets you write a file
296 *
297 * @access public
298 * @param string the file name
299 * @param string the data to be encoded
300 * @return bool
301 */
302 function archive($filepath)
303 {
304 if ( ! ($fp = @fopen($filepath, "wb")))
305 {
306 return FALSE;
307 }
308
309 flock($fp, LOCK_EX);
310 fwrite($fp, $this->get_zip());
311 flock($fp, LOCK_UN);
312 fclose($fp);
313
314 return TRUE;
315 }
316
317 // --------------------------------------------------------------------
318
319 /**
320 * Download
321 *
322 * @access public
323 * @param string the file name
324 * @param string the data to be encoded
325 * @return bool
326 */
327 function download($filename = 'backup.zip')
328 {
329 if ( ! preg_match("|.+?\.zip$|", $filename))
330 {
331 $filename .= '.zip';
332 }
333
334 if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
335 {
336 header('Content-Type: application/x-zip');
337 header('Content-Disposition: inline; filename="'.$filename.'"');
338 header('Expires: 0');
339 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
340 header("Content-Transfer-Encoding: binary");
341 header('Pragma: public');
342 header("Content-Length: ".strlen($this->get_zip()));
343 }
344 else
345 {
346 header('Content-Type: application/x-zip');
347 header('Content-Disposition: attachment; filename="'.$filename.'"');
348 header("Content-Transfer-Encoding: binary");
349 header('Expires: 0');
350 header('Pragma: no-cache');
351 header("Content-Length: ".strlen($this->get_zip()));
352 }
353
354 echo $this->get_zip();
355 }
356
357 // --------------------------------------------------------------------
358
359 /**
360 * Initialize Data
361 *
362 * Lets you clear current zip data. Useful if you need to create
363 * multiple zips with different data.
364 *
365 * @access public
366 * @return void
367 */
368 function clear_data()
369 {
370 $this->zipfile = '';
371 $this->zipdata = array();
372 $this->directory = array();
373 $this->offset = array();
374 }
375
376}
adminae58a5d2006-09-30 19:25:07 +0000377?>