| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 | <?php/** * CodeIgniter * * An open source application development framework for PHP * * This content is released under the MIT License (MIT) * * Copyright (c) 2014 - 2017, British Columbia Institute of Technology * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * @package	CodeIgniter * @author	EllisLab Dev Team * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) * @license	http://opensource.org/licenses/MIT	MIT License * @link	https://codeigniter.com * @since	Version 1.0.0 * @filesource */defined('BASEPATH') OR exit('No direct script access allowed');/** * Zip Compression Class * * This class is based on a library I found at Zend: * http://www.zend.com/codex.php?id=696&single=1 * * The original library is a little rough around the edges so I * refactored it and added several additional methods -- Rick Ellis * * @package		CodeIgniter * @subpackage	Libraries * @category	Encryption * @author		EllisLab Dev Team * @link		https://codeigniter.com/user_guide/libraries/zip.html */class CI_Zip {	/**	 * Zip data in string form	 *	 * @var string	 */	public $zipdata = '';	/**	 * Zip data for a directory in string form	 *	 * @var string	 */	public $directory = '';	/**	 * Number of files/folder in zip file	 *	 * @var int	 */	public $entries = 0;	/**	 * Number of files in zip	 *	 * @var int	 */	public $file_num = 0;	/**	 * relative offset of local header	 *	 * @var int	 */	public $offset = 0;	/**	 * Reference to time at init	 *	 * @var int	 */	public $now;	/**	 * The level of compression	 *	 * Ranges from 0 to 9, with 9 being the highest level.	 *	 * @var	int	 */	public $compression_level = 2;	/**	 * mbstring.func_overload flag	 *	 * @var	bool	 */	protected static $func_overload;	/**	 * Initialize zip compression class	 *	 * @return	void	 */	public function __construct()	{		isset(self::$func_overload) OR self::$func_overload = (extension_loaded('mbstring') && ini_get('mbstring.func_overload'));		$this->now = time();		log_message('info', 'Zip Compression Class Initialized');	}	// --------------------------------------------------------------------	/**	 * Add Directory	 *	 * Lets you add a virtual directory into which you can place files.	 *	 * @param	mixed	$directory	the directory name. Can be string or array	 * @return	void	 */	public function add_dir($directory)	{		foreach ((array) $directory as $dir)		{			if ( ! preg_match('|.+/$|', $dir))			{				$dir .= '/';			}			$dir_time = $this->_get_mod_time($dir);			$this->_add_dir($dir, $dir_time['file_mtime'], $dir_time['file_mdate']);		}	}	// --------------------------------------------------------------------	/**	 * Get file/directory modification time	 *	 * If this is a newly created file/dir, we will set the time to 'now'	 *	 * @param	string	$dir	path to file	 * @return	array	filemtime/filemdate	 */	protected function _get_mod_time($dir)	{		// filemtime() may return false, but raises an error for non-existing files		$date = file_exists($dir) ? getdate(filemtime($dir)) : getdate($this->now);		return array(			'file_mtime' => ($date['hours'] << 11) + ($date['minutes'] << 5) + $date['seconds'] / 2,			'file_mdate' => (($date['year'] - 1980) << 9) + ($date['mon'] << 5) + $date['mday']		);	}	// --------------------------------------------------------------------	/**	 * Add Directory	 *	 * @param	string	$dir	the directory name	 * @param	int	$file_mtime	 * @param	int	$file_mdate	 * @return	void	 */	protected function _add_dir($dir, $file_mtime, $file_mdate)	{		$dir = str_replace('\\', '/', $dir);		$this->zipdata .=			"\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00"			.pack('v', $file_mtime)			.pack('v', $file_mdate)			.pack('V', 0) // crc32			.pack('V', 0) // compressed filesize			.pack('V', 0) // uncompressed filesize			.pack('v', self::strlen($dir)) // length of pathname			.pack('v', 0) // extra field length			.$dir			// below is "data descriptor" segment			.pack('V', 0) // crc32			.pack('V', 0) // compressed filesize			.pack('V', 0); // uncompressed filesize		$this->directory .=			"\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00"			.pack('v', $file_mtime)			.pack('v', $file_mdate)			.pack('V',0) // crc32			.pack('V',0) // compressed filesize			.pack('V',0) // uncompressed filesize			.pack('v', self::strlen($dir)) // length of pathname			.pack('v', 0) // extra field length			.pack('v', 0) // file comment length			.pack('v', 0) // disk number start			.pack('v', 0) // internal file attributes			.pack('V', 16) // external file attributes - 'directory' bit set			.pack('V', $this->offset) // relative offset of local header			.$dir;		$this->offset = self::strlen($this->zipdata);		$this->entries++;	}	// --------------------------------------------------------------------	/**	 * Add Data to Zip	 *	 * Lets you add files to the archive. If the path is included	 * in the filename it will be placed within a directory. Make	 * sure you use add_dir() first to create the folder.	 *	 * @param	mixed	$filepath	A single filepath or an array of file => data pairs	 * @param	string	$data		Single file contents	 * @return	void	 */	public function add_data($filepath, $data = NULL)	{		if (is_array($filepath))		{			foreach ($filepath as $path => $data)			{				$file_data = $this->_get_mod_time($path);				$this->_add_data($path, $data, $file_data['file_mtime'], $file_data['file_mdate']);			}		}		else		{			$file_data = $this->_get_mod_time($filepath);			$this->_add_data($filepath, $data, $file_data['file_mtime'], $file_data['file_mdate']);		}	}	// --------------------------------------------------------------------	/**	 * Add Data to Zip	 *	 * @param	string	$filepath	the file name/path	 * @param	string	$data	the data to be encoded	 * @param	int	$file_mtime	 * @param	int	$file_mdate	 * @return	void	 */	protected function _add_data($filepath, $data, $file_mtime, $file_mdate)	{		$filepath = str_replace('\\', '/', $filepath);		$uncompressed_size = self::strlen($data);		$crc32  = crc32($data);		$gzdata = self::substr(gzcompress($data, $this->compression_level), 2, -4);		$compressed_size = self::strlen($gzdata);		$this->zipdata .=			"\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00"			.pack('v', $file_mtime)			.pack('v', $file_mdate)			.pack('V', $crc32)			.pack('V', $compressed_size)			.pack('V', $uncompressed_size)			.pack('v', self::strlen($filepath)) // length of filename			.pack('v', 0) // extra field length			.$filepath			.$gzdata; // "file data" segment		$this->directory .=			"\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00"			.pack('v', $file_mtime)			.pack('v', $file_mdate)			.pack('V', $crc32)			.pack('V', $compressed_size)			.pack('V', $uncompressed_size)			.pack('v', self::strlen($filepath)) // length of filename			.pack('v', 0) // extra field length			.pack('v', 0) // file comment length			.pack('v', 0) // disk number start			.pack('v', 0) // internal file attributes			.pack('V', 32) // external file attributes - 'archive' bit set			.pack('V', $this->offset) // relative offset of local header			.$filepath;		$this->offset = self::strlen($this->zipdata);		$this->entries++;		$this->file_num++;	}	// --------------------------------------------------------------------	/**	 * Read the contents of a file and add it to the zip	 *	 * @param	string	$path	 * @param	bool	$archive_filepath	 * @return	bool	 */	public function read_file($path, $archive_filepath = FALSE)	{		if (file_exists($path) && FALSE !== ($data = file_get_contents($path)))		{			if (is_string($archive_filepath))			{				$name = str_replace('\\', '/', $archive_filepath);			}			else			{				$name = str_replace('\\', '/', $path);				if ($archive_filepath === FALSE)				{					$name = preg_replace('|.*/(.+)|', '\\1', $name);				}			}			$this->add_data($name, $data);			return TRUE;		}		return FALSE;	}	// ------------------------------------------------------------------------	/**	 * Read a directory and add it to the zip.	 *	 * This function recursively reads a folder and everything it contains (including	 * sub-folders) and creates a zip based on it. Whatever directory structure	 * is in the original file path will be recreated in the zip file.	 *	 * @param	string	$path	path to source directory	 * @param	bool	$preserve_filepath	 * @param	string	$root_path	 * @return	bool	 */	public function read_dir($path, $preserve_filepath = TRUE, $root_path = NULL)	{		$path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;		if ( ! $fp = @opendir($path))		{			return FALSE;		}		// Set the original directory root for child dir's to use as relative		if ($root_path === NULL)		{			$root_path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, dirname($path)).DIRECTORY_SEPARATOR;		}		while (FALSE !== ($file = readdir($fp)))		{			if ($file[0] === '.')			{				continue;			}			if (is_dir($path.$file))			{				$this->read_dir($path.$file.DIRECTORY_SEPARATOR, $preserve_filepath, $root_path);			}			elseif (FALSE !== ($data = file_get_contents($path.$file)))			{				$name = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);				if ($preserve_filepath === FALSE)				{					$name = str_replace($root_path, '', $name);				}				$this->add_data($name.$file, $data);			}		}		closedir($fp);		return TRUE;	}	// --------------------------------------------------------------------	/**	 * Get the Zip file	 *	 * @return	string	(binary encoded)	 */	public function get_zip()	{		// Is there any data to return?		if ($this->entries === 0)		{			return FALSE;		}		return $this->zipdata			.$this->directory."\x50\x4b\x05\x06\x00\x00\x00\x00"			.pack('v', $this->entries) // total # of entries "on this disk"			.pack('v', $this->entries) // total # of entries overall			.pack('V', self::strlen($this->directory)) // size of central dir			.pack('V', self::strlen($this->zipdata)) // offset to start of central dir			."\x00\x00"; // .zip file comment length	}	// --------------------------------------------------------------------	/**	 * Write File to the specified directory	 *	 * Lets you write a file	 *	 * @param	string	$filepath	the file name	 * @return	bool	 */	public function archive($filepath)	{		if ( ! ($fp = @fopen($filepath, 'w+b')))		{			return FALSE;		}		flock($fp, LOCK_EX);		for ($result = $written = 0, $data = $this->get_zip(), $length = self::strlen($data); $written < $length; $written += $result)		{			if (($result = fwrite($fp, self::substr($data, $written))) === FALSE)			{				break;			}		}		flock($fp, LOCK_UN);		fclose($fp);		return is_int($result);	}	// --------------------------------------------------------------------	/**	 * Download	 *	 * @param	string	$filename	the file name	 * @return	void	 */	public function download($filename = 'backup.zip')	{		if ( ! preg_match('|.+?\.zip$|', $filename))		{			$filename .= '.zip';		}		get_instance()->load->helper('download');		$get_zip = $this->get_zip();		$zip_content =& $get_zip;		force_download($filename, $zip_content);	}	// --------------------------------------------------------------------	/**	 * Initialize Data	 *	 * Lets you clear current zip data. Useful if you need to create	 * multiple zips with different data.	 *	 * @return	CI_Zip	 */	public function clear_data()	{		$this->zipdata = '';		$this->directory = '';		$this->entries = 0;		$this->file_num = 0;		$this->offset = 0;		return $this;	}	// --------------------------------------------------------------------	/**	 * Byte-safe strlen()	 *	 * @param	string	$str	 * @return	int	 */	protected static function strlen($str)	{		return (self::$func_overload)			? mb_strlen($str, '8bit')			: strlen($str);	}	// --------------------------------------------------------------------	/**	 * Byte-safe substr()	 *	 * @param	string	$str	 * @param	int	$start	 * @param	int	$length	 * @return	string	 */	protected static function substr($str, $start, $length = NULL)	{		if (self::$func_overload)		{			// mb_substr($str, $start, null, '8bit') returns an empty			// string on PHP 5.3			isset($length) OR $length = ($start >= 0 ? self::strlen($str) - $start : -$start);			return mb_substr($str, $start, $length, '8bit');		}		return isset($length)			? substr($str, $start, $length)			: substr($str, $start);	}}
 |