<?php
/**
* This class is an adaption of a zip library from phpmyadmin v2.4.
* PHPMyAdmin was released under the terms of GPLv2.
*
* The origins and license of this library itself are pretty unclear.
* For the moment we can only assume, that the GPLv2 is the correct
* license for all this.
*
* This adaption of a library therfor is released on the same terms
* as stated by the phpmyadmin project.
*
* For a complete copy of the license agreement please see either
*
* All adaptions where made by Jens Clasen <juwe@clasennet.de>.
* This file was submitted to the phpmyadmin project and published
*
* The author kindly asks to keep the link and author name in place
* when using this adaption in other projects.
*
* ####### Original header & Credits #########
*
* Zip file creation class.
* Makes zip files.
*
* Based on :
*
* http://www.zend.com/codex.php?id=535&single=1
* By Eric Mueller <eric@themepark.com>
*
* http://www.zend.com/codex.php?id=470&single=1
* by Denis125 <webmaster@atlant.ru>
*
* a patch from Peter Listiak <mlady@users.sourceforge.net> for last modified
* date and time of the compressed file
*
*
* @access public
*/
class zipfile
{
/**
* flag to show if further records can be attached to this archiv.
* @var bool can_take_records
*/
var $can_take_records=true;
/**
* central directory record count
* @var int $cd_record_cnt;
*/
var $cd_record_cnt=0;
/**
* End of central directory record
*
* @var string $eof_ctrl_dir
*/
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
/**
* Last offset position
*
* @var integer $old_offset
*/
var $old_offset = 0;
/**
* File descriptor for temporary storage of data segments
* @var ressource $data_fd
*/
var $data_fd = null;
/**
* File descriptor for temporary storage of central directory
* @var ressource $directory_fd
*/
var $directory_fd = null;
/**
* total amount of raw data
* @var float $data_size
*/
var $data_size = 0;
/**
* total amount of data in central directory
* @var float $cd_size
*/
var $cd_size = 0;
/**
* Prepares temporary files for storing data segments and central directory
* @access private
*/
function prepareTempfiles()
{
}
/**
* Converts an Unix timestamp to a four byte DOS date and time format (date
* in high two bytes, time in low two bytes allowing magnitude comparison).
*
* @param integer the current Unix timestamp
*
* @return integer the current date in a four byte DOS format
*
* @access private
*/
function unix2DosTime($unixtime = 0) {
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
} // end if
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method
/**
* This method marks an archive as complete and ready for sending.
* @access private
*/
function markComplete()
{
$this->can_take_records=false;
}
/**
* Returns zip archiv "footer"
* @access private
*/
function getFooter()
{
if($this->can_take_records)
{
trigger_error('This zip archiv was not marked complete. Footer can not be created'
.' when further records might be attached later!',
E_USER_ERROR);
return;
}
return $this -> eof_ctrl_dir .
pack('v',
$this ->
cd_record_cnt) .
// total # of entries "on this disk"
pack('v',
$this ->
cd_record_cnt) .
// total # of entries overall
pack('V',
$this->
cd_size) .
// size of central dir
pack('V',
$this->
data_size) .
// offset to start of central dir
"\x00\x00"; // .zip file comment length
}
/**
* Adds "file" to archive
*
* @param string file contents
* @param string name of the file in the archive (may contains the path)
* @param integer the current timestamp
*
* @access public
*/
function addFile($data, $name, $time = 0)
{
if(!$this->can_take_records)
{
trigger_error('This zip archiv was allready marked complete. No further files can be attached!',
E_USER_ERROR);
return;
}
$this->prepareTempfiles();
$dtime =
dechex($this->
unix2DosTime($time));
$hexdtime = '\x' . $dtime[6] . $dtime[7]
. '\x' . $dtime[4] . $dtime[5]
. '\x' . $dtime[2] . $dtime[3]
. '\x' . $dtime[0] . $dtime[1];
eval('$hexdtime = "' .
$hexdtime .
'";');
// store position in data file for later usage:
$data_segment_start_pos=
ftell($this->
data_fd);
fputs($this->
data_fd,
"\x50\x4b\x03\x04");
fputs($this->
data_fd,
"\x14\x00");
// ver needed to extract
fputs($this->
data_fd,
"\x00\x00");
// gen purpose bit flag
fputs($this->
data_fd,
"\x08\x00");
// compression method
fputs($this->
data_fd,
$hexdtime);
// last mod time and date
// "local file header" segment
// $data uses quite a lot of memory. Since we don't need it any longer,
// we can safely remove it at this point.
fputs($this->
data_fd,
pack('V',
$crc));
// crc32
fputs($this->
data_fd,
pack('V',
$c_len));
// compressed filesize
fputs($this->
data_fd,
pack('V',
$unc_len));
// uncompressed filesize
fputs($this->
data_fd,
pack('v',
0));
// extra field length
fputs($this->
data_fd,
$name);
// "file data" segment
fputs($this->
data_fd,
$zdata);
// $zdata is quite large as well, therfor we better remove it
// store size for later usage
$data_segment_length=
ftell($this->
data_fd)-
$data_segment_start_pos;
// now add to central directory record
fputs($this->
directory_fd,
"\x50\x4b\x01\x02");
fputs($this->
directory_fd,
"\x00\x00");
// version made by
fputs($this->
directory_fd,
"\x14\x00");
// version needed to extract
fputs($this->
directory_fd,
"\x00\x00");
// gen purpose bit flag
fputs($this->
directory_fd,
"\x08\x00");
// compression method
fputs($this->
directory_fd,
$hexdtime);
// last mod time & date
fputs($this->
directory_fd,
pack('V',
$crc));
// crc32
fputs($this->
directory_fd,
pack('V',
$c_len));
// compressed filesize
fputs($this->
directory_fd,
pack('V',
$unc_len));
// uncompressed filesize
fputs($this->
directory_fd,
pack('v',
strlen($name) ));
// length of filename
fputs($this->
directory_fd,
pack('v',
0 ));
// extra field length
fputs($this->
directory_fd,
pack('v',
0 ));
// file comment length
fputs($this->
directory_fd,
pack('v',
0 ));
// disk number start
fputs($this->
directory_fd,
pack('v',
0 ));
// internal file attributes
fputs($this->
directory_fd,
pack('V',
32 ));
// external file attributes - 'archive' bit set
fputs($this->
directory_fd,
pack('V',
$this ->
old_offset ));
// relative offset of local header
$this -> old_offset += $data_segment_length;
fputs($this->
directory_fd,
$name);
// optional extra field, file comment goes here
// save to central directory
$this -> cd_record_cnt++;
// update sizes
$this->
cd_size=
ftell($this->
directory_fd);
$this->
data_size=
ftell($this->
data_fd);
} // end of the 'addFile()' method
/**
* Dumps out file (only kept for backwards compatibility)
*
* @return string the zipped file
*
* @access public
*/
{
$this->prepareTempfiles();
$this->markComplete();
return
fread($this->
data_fd,
$this->
data_size).
fread($this->
directory_fd,
$this->
cd_size).
$this->getFooter();
} // end of the 'file()' method
/**
* Send zip archive to client
* @param string $archiveName
* @access public
*/
function sendFile($archiveName)
{
$this->prepareTempfiles();
$this->markComplete();
$footer=$this->getFooter();
header('Content-type: application/x-zip');
header('Content-disposition: attachment; filename='.
$archiveName);
header('Content-length: '.
(strlen($footer)+
$this->
cd_size+
$this->
data_size));
}
} // end of the 'zipfile' class
?>