Subversion-Projekte lars-tiefland.ci

Revision

Revision 1257 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2016, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Zip Compression Class
42
 *
43
 * This class is based on a library I found at Zend:
44
 * http://www.zend.com/codex.php?id=696&single=1
45
 *
46
 * The original library is a little rough around the edges so I
47
 * refactored it and added several additional methods -- Rick Ellis
48
 *
49
 * @package		CodeIgniter
50
 * @subpackage	Libraries
51
 * @category	Encryption
52
 * @author		EllisLab Dev Team
53
 * @link		https://codeigniter.com/user_guide/libraries/zip.html
54
 */
55
class CI_Zip {
56
 
57
	/**
58
	 * Zip data in string form
59
	 *
60
	 * @var string
61
	 */
62
	public $zipdata = '';
63
 
64
	/**
65
	 * Zip data for a directory in string form
66
	 *
67
	 * @var string
68
	 */
69
	public $directory = '';
70
 
71
	/**
72
	 * Number of files/folder in zip file
73
	 *
74
	 * @var int
75
	 */
76
	public $entries = 0;
77
 
78
	/**
79
	 * Number of files in zip
80
	 *
81
	 * @var int
82
	 */
83
	public $file_num = 0;
84
 
85
	/**
86
	 * relative offset of local header
87
	 *
88
	 * @var int
89
	 */
90
	public $offset = 0;
91
 
92
	/**
93
	 * Reference to time at init
94
	 *
95
	 * @var int
96
	 */
97
	public $now;
98
 
99
	/**
100
	 * The level of compression
101
	 *
102
	 * Ranges from 0 to 9, with 9 being the highest level.
103
	 *
104
	 * @var	int
105
	 */
106
	public $compression_level = 2;
107
 
108
	/**
109
	 * Initialize zip compression class
110
	 *
111
	 * @return	void
112
	 */
113
	public function __construct()
114
	{
115
		$this->now = time();
116
		log_message('info', 'Zip Compression Class Initialized');
117
	}
118
 
119
	// --------------------------------------------------------------------
120
 
121
	/**
122
	 * Add Directory
123
	 *
124
	 * Lets you add a virtual directory into which you can place files.
125
	 *
126
	 * @param	mixed	$directory	the directory name. Can be string or array
127
	 * @return	void
128
	 */
129
	public function add_dir($directory)
130
	{
131
		foreach ((array) $directory as $dir)
132
		{
133
			if ( ! preg_match('|.+/$|', $dir))
134
			{
135
				$dir .= '/';
136
			}
137
 
138
			$dir_time = $this->_get_mod_time($dir);
139
			$this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);
140
		}
141
	}
142
 
143
	// --------------------------------------------------------------------
144
 
145
	/**
146
	 * Get file/directory modification time
147
	 *
148
	 * If this is a newly created file/dir, we will set the time to 'now'
149
	 *
150
	 * @param	string	$dir	path to file
151
	 * @return	array	filemtime/filemdate
152
	 */
153
	protected function _get_mod_time($dir)
154
	{
155
		// filemtime() may return false, but raises an error for non-existing files
156
		$date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now);
157
 
158
		return array(
159
			'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2,
160
			'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday']
161
		);
162
	}
163
 
164
	// --------------------------------------------------------------------
165
 
166
	/**
167
	 * Add Directory
168
	 *
169
	 * @param	string	$dir	the directory name
170
	 * @param	int	$file_mtime
171
	 * @param	int	$file_mdate
172
	 * @return	void
173
	 */
174
	protected function _add_dir($dir, $file_mtime, $file_mdate)
175
	{
176
		$dir = str_replace('\\', '/', $dir);
177
 
178
		$this->zipdata .=
179
			"\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00"
180
			.pack('v', $file_mtime)
181
			.pack('v', $file_mdate)
182
			.pack('V', 0) // crc32
183
			.pack('V', 0) // compressed filesize
184
			.pack('V', 0) // uncompressed filesize
185
			.pack('v', strlen($dir)) // length of pathname
186
			.pack('v', 0) // extra field length
187
			.$dir
188
			// below is "data descriptor" segment
189
			.pack('V', 0) // crc32
190
			.pack('V', 0) // compressed filesize
191
			.pack('V', 0); // uncompressed filesize
192
 
193
		$this->directory .=
194
			"\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00"
195
			.pack('v', $file_mtime)
196
			.pack('v', $file_mdate)
197
			.pack('V',0) // crc32
198
			.pack('V',0) // compressed filesize
199
			.pack('V',0) // uncompressed filesize
200
			.pack('v', strlen($dir)) // length of pathname
201
			.pack('v', 0) // extra field length
202
			.pack('v', 0) // file comment length
203
			.pack('v', 0) // disk number start
204
			.pack('v', 0) // internal file attributes
205
			.pack('V', 16) // external file attributes - 'directory' bit set
206
			.pack('V', $this->offset) // relative offset of local header
207
			.$dir;
208
 
209
		$this->offset = strlen($this->zipdata);
210
		$this->entries++;
211
	}
212
 
213
	// --------------------------------------------------------------------
214
 
215
	/**
216
	 * Add Data to Zip
217
	 *
218
	 * Lets you add files to the archive. If the path is included
219
	 * in the filename it will be placed within a directory. Make
220
	 * sure you use add_dir() first to create the folder.
221
	 *
222
	 * @param	mixed	$filepath	A single filepath or an array of file => data pairs
223
	 * @param	string	$data		Single file contents
224
	 * @return	void
225
	 */
226
	public function add_data($filepath, $data = NULL)
227
	{
228
		if (is_array($filepath))
229
		{
230
			foreach ($filepath as $path => $data)
231
			{
232
				$file_data = $this->_get_mod_time($path);
233
				$this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);
234
			}
235
		}
236
		else
237
		{
238
			$file_data = $this->_get_mod_time($filepath);
239
			$this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);
240
		}
241
	}
242
 
243
	// --------------------------------------------------------------------
244
 
245
	/**
246
	 * Add Data to Zip
247
	 *
248
	 * @param	string	$filepath	the file name/path
249
	 * @param	string	$data	the data to be encoded
250
	 * @param	int	$file_mtime
251
	 * @param	int	$file_mdate
252
	 * @return	void
253
	 */
254
	protected function _add_data($filepath, $data, $file_mtime, $file_mdate)
255
	{
256
		$filepath = str_replace('\\', '/', $filepath);
257
 
258
		$uncompressed_size = strlen($data);
259
		$crc32  = crc32($data);
260
		$gzdata = substr(gzcompress($data, $this->compression_level), 2, -4);
261
		$compressed_size = strlen($gzdata);
262
 
263
		$this->zipdata .=
264
			"\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00"
265
			.pack('v', $file_mtime)
266
			.pack('v', $file_mdate)
267
			.pack('V', $crc32)
268
			.pack('V', $compressed_size)
269
			.pack('V', $uncompressed_size)
270
			.pack('v', strlen($filepath)) // length of filename
271
			.pack('v', 0) // extra field length
272
			.$filepath
273
			.$gzdata; // "file data" segment
274
 
275
		$this->directory .=
276
			"\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00"
277
			.pack('v', $file_mtime)
278
			.pack('v', $file_mdate)
279
			.pack('V', $crc32)
280
			.pack('V', $compressed_size)
281
			.pack('V', $uncompressed_size)
282
			.pack('v', strlen($filepath)) // length of filename
283
			.pack('v', 0) // extra field length
284
			.pack('v', 0) // file comment length
285
			.pack('v', 0) // disk number start
286
			.pack('v', 0) // internal file attributes
287
			.pack('V', 32) // external file attributes - 'archive' bit set
288
			.pack('V', $this->offset) // relative offset of local header
289
			.$filepath;
290
 
291
		$this->offset = strlen($this->zipdata);
292
		$this->entries++;
293
		$this->file_num++;
294
	}
295
 
296
	// --------------------------------------------------------------------
297
 
298
	/**
299
	 * Read the contents of a file and add it to the zip
300
	 *
301
	 * @param	string	$path
302
	 * @param	bool	$archive_filepath
303
	 * @return	bool
304
	 */
305
	public function read_file($path, $archive_filepath = FALSE)
306
	{
307
		if (file_exists($path) && FALSE !== ($data = file_get_contents($path)))
308
		{
309
			if (is_string($archive_filepath))
310
			{
311
				$name = str_replace('\\', '/', $archive_filepath);
312
			}
313
			else
314
			{
315
				$name = str_replace('\\', '/', $path);
316
 
317
				if ($archive_filepath === FALSE)
318
				{
319
					$name = preg_replace('|.*/(.+)|', '\\1', $name);
320
				}
321
			}
322
 
323
			$this->add_data($name, $data);
324
			return TRUE;
325
		}
326
 
327
		return FALSE;
328
	}
329
 
330
	// ------------------------------------------------------------------------
331
 
332
	/**
333
	 * Read a directory and add it to the zip.
334
	 *
335
	 * This function recursively reads a folder and everything it contains (including
336
	 * sub-folders) and creates a zip based on it. Whatever directory structure
337
	 * is in the original file path will be recreated in the zip file.
338
	 *
339
	 * @param	string	$path	path to source directory
340
	 * @param	bool	$preserve_filepath
341
	 * @param	string	$root_path
342
	 * @return	bool
343
	 */
344
	public function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)
345
	{
346
		$path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
347
		if ( ! $fp = @opendir($path))
348
		{
349
			return FALSE;
350
		}
351
 
352
		// Set the original directory root for child dir's to use as relative
353
		if ($root_path === NULL)
354
		{
355
			$root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR;
356
		}
357
 
358
		while (FALSE !== ($file = readdir($fp)))
359
		{
360
			if ($file[0] === '.')
361
			{
362
				continue;
363
			}
364
 
365
			if (is_dir($path.$file))
366
			{
367
				$this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path);
368
			}
369
			elseif (FALSE !== ($data = file_get_contents($path.$file)))
370
			{
371
				$name = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
372
				if ($preserve_filepath === FALSE)
373
				{
374
					$name = str_replace($root_path, '', $name);
375
				}
376
 
377
				$this->add_data($name.$file, $data);
378
			}
379
		}
380
 
381
		closedir($fp);
382
		return TRUE;
383
	}
384
 
385
	// --------------------------------------------------------------------
386
 
387
	/**
388
	 * Get the Zip file
389
	 *
390
	 * @return	string	(binary encoded)
391
	 */
392
	public function get_zip()
393
	{
394
		// Is there any data to return?
395
		if ($this->entries === 0)
396
		{
397
			return FALSE;
398
		}
399
 
400
		return $this->zipdata
401
			.$this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"
402
			.pack('v', $this->entries) // total # of entries "on this disk"
403
			.pack('v', $this->entries) // total # of entries overall
404
			.pack('V', strlen($this->directory)) // size of central dir
405
			.pack('V', strlen($this->zipdata)) // offset to start of central dir
406
			."\x00\x00"; // .zip file comment length
407
	}
408
 
409
	// --------------------------------------------------------------------
410
 
411
	/**
412
	 * Write File to the specified directory
413
	 *
414
	 * Lets you write a file
415
	 *
416
	 * @param	string	$filepath	the file name
417
	 * @return	bool
418
	 */
419
	public function archive($filepath)
420
	{
421
		if ( ! ($fp = @fopen($filepath, 'w+b')))
422
		{
423
			return FALSE;
424
		}
425
 
426
		flock($fp, LOCK_EX);
427
 
428
		for ($result = $written = 0, $data = $this->get_zip(), $length = strlen($data); $written < $length; $written += $result)
429
		{
430
			if (($result = fwrite($fp, substr($data, $written))) === FALSE)
431
			{
432
				break;
433
			}
434
		}
435
 
436
		flock($fp, LOCK_UN);
437
		fclose($fp);
438
 
439
		return is_int($result);
440
	}
441
 
442
	// --------------------------------------------------------------------
443
 
444
	/**
445
	 * Download
446
	 *
447
	 * @param	string	$filename	the file name
448
	 * @return	void
449
	 */
450
	public function download($filename = 'backup.zip')
451
	{
452
		if ( ! preg_match('|.+?\.zip$|', $filename))
453
		{
454
			$filename .= '.zip';
455
		}
456
 
457
		get_instance()->load->helper('download');
458
		$get_zip = $this->get_zip();
459
		$zip_content =& $get_zip;
460
 
461
		force_download($filename, $zip_content);
462
	}
463
 
464
	// --------------------------------------------------------------------
465
 
466
	/**
467
	 * Initialize Data
468
	 *
469
	 * Lets you clear current zip data. Useful if you need to create
470
	 * multiple zips with different data.
471
	 *
472
	 * @return	CI_Zip
473
	 */
474
	public function clear_data()
475
	{
476
		$this->zipdata = '';
477
		$this->directory = '';
478
		$this->entries = 0;
479
		$this->file_num = 0;
480
		$this->offset = 0;
481
		return $this;
482
	}
483
 
484
}