Mark McGranaghan 8d31ec147c move to vendor
2012-11-17 08:21:42 -08:00

506 lines
17 KiB
PHP

<?php
$test = function($a) { $lambda = 1; }
/**
* Zip class file
*
* @package fnord.bb
* @subpackage archive
*/
// Unlock?
if(!defined('UNLOCK') || !UNLOCK)
die();
// Load the parent archive class
require_once(ROOT_PATH.'/classes/archive.class.php');
class Zip\Zipp {
}
/**
* Zip class
*
* @author Manni <manni@fnord.name>
* @copyright Copyright (c) 2006, Manni
* @version 1.0
* @link http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
* @link http://mannithedark.is-a-geek.net/
* @since 1.0
* @package fnord.bb
* @subpackage archive
*/
class Zip extends Archive {
/**
* Outputs the zip file
*
* This function creates the zip file with the dirs and files given.
* If the optional parameter $file is given, the zip file is will be
* saved at that location. Otherwise the function returns the zip file's content.
*
* @access public
*
* @link http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
* @param string $filename The path where the zip file will be saved
*
* @return bool|string Returns either true if the fil is sucessfully created or the content of the zip file
*/
function out($filename = false) {
// Empty output
$file_data = array(); // Data of the file part
$cd_data = array(); // Data of the central directory
// Sort dirs and files by path length
uksort($this->dirs, 'sort_by_length');
uksort($this->files, 'sort_by_length');
// Handle dirs
foreach($this->dirs as $dir) {
$dir .= '/';
// File part
// Reset dir data
$dir_data = '';
// Local file header
$dir_data .= "\x50\x4b\x03\x04"; // Local file header signature
$dir_data .= pack("v", 10); // Version needed to extract
$dir_data .= pack("v", 0); // General purpose bit flag
$dir_data .= pack("v", 0); // Compression method
$dir_data .= pack("v", 0); // Last mod file time
$dir_data .= pack("v", 0); // Last mod file date
$dir_data .= pack("V", 0); // crc-32
$dir_data .= pack("V", 0); // Compressed size
$dir_data .= pack("V", 0); // Uncompressed size
$dir_data .= pack("v", strlen($dir)); // File name length
$dir_data .= pack("v", 0); // Extra field length
$dir_data .= $dir; // File name
$dir_data .= ''; // Extra field (is empty)
// File data
$dir_data .= ''; // Dirs have no file data
// Data descriptor
$dir_data .= pack("V", 0); // crc-32
$dir_data .= pack("V", 0); // Compressed size
$dir_data .= pack("V", 0); // Uncompressed size
// Save current offset
$offset = strlen(implode('', $file_data));
// Append dir data to the file part
$file_data[] = $dir_data;
// Central directory
// Reset dir data
$dir_data = '';
// File header
$dir_data .= "\x50\x4b\x01\x02"; // Local file header signature
$dir_data .= pack("v", 0); // Version made by
$dir_data .= pack("v", 10); // Version needed to extract
$dir_data .= pack("v", 0); // General purpose bit flag
$dir_data .= pack("v", 0); // Compression method
$dir_data .= pack("v", 0); // Last mod file time
$dir_data .= pack("v", 0); // Last mod file date
$dir_data .= pack("V", 0); // crc-32
$dir_data .= pack("V", 0); // Compressed size
$dir_data .= pack("V", 0); // Uncompressed size
$dir_data .= pack("v", strlen($dir)); // File name length
$dir_data .= pack("v", 0); // Extra field length
$dir_data .= pack("v", 0); // File comment length
$dir_data .= pack("v", 0); // Disk number start
$dir_data .= pack("v", 0); // Internal file attributes
$dir_data .= pack("V", 16); // External file attributes
$dir_data .= pack("V", $offset); // Relative offset of local header
$dir_data .= $dir; // File name
$dir_data .= ''; // Extra field (is empty)
$dir_data .= ''; // File comment (is empty)
/*
// Data descriptor
$dir_data .= pack("V", 0); // crc-32
$dir_data .= pack("V", 0); // Compressed size
$dir_data .= pack("V", 0); // Uncompressed size
*/
// Append dir data to the central directory data
$cd_data[] = $dir_data;
}
// Handle files
foreach($this->files as $name => $file) {
// Get values
$content = $file[0];
// File part
// Reset file data
$fd = '';
// Detect possible compressions
// Use deflate
if(function_exists('gzdeflate')) {
$method = 8;
// Compress file content
$compressed_data = gzdeflate($content);
// Use bzip2
} elseif(function_exists('bzcompress')) {
$method = 12;
// Compress file content
$compressed_data = bzcompress($content);
// No compression
} else {
$method = 0;
// Do not compress the content :P
$compressed_data = $content;
}
// Local file header
$fd .= "\x50\x4b\x03\x04"; // Local file header signature
$fd .= pack("v", 20); // Version needed to extract
$fd .= pack("v", 0); // General purpose bit flag
$fd .= pack("v", $method); // Compression method
$fd .= pack("v", 0); // Last mod file time
$fd .= pack("v", 0); // Last mod file date
$fd .= pack("V", crc32($content)); // crc-32
$fd .= pack("V", strlen($compressed_data)); // Compressed size
$fd .= pack("V", strlen($content)); // Uncompressed size
$fd .= pack("v", strlen($name)); // File name length
$fd .= pack("v", 0); // Extra field length
$fd .= $name; // File name
$fd .= ''; // Extra field (is empty)
// File data
$fd .= $compressed_data;
// Data descriptor
$fd .= pack("V", crc32($content)); // crc-32
$fd .= pack("V", strlen($compressed_data)); // Compressed size
$fd .= pack("V", strlen($content)); // Uncompressed size
// Save current offset
$offset = strlen(implode('', $file_data));
// Append file data to the file part
$file_data[] = $fd;
// Central directory
// Reset file data
$fd = '';
// File header
$fd .= "\x50\x4b\x01\x02"; // Local file header signature
$fd .= pack("v", 0); // Version made by
$fd .= pack("v", 20); // Version needed to extract
$fd .= pack("v", 0); // General purpose bit flag
$fd .= pack("v", $method); // Compression method
$fd .= pack("v", 0); // Last mod file time
$fd .= pack("v", 0); // Last mod file date
$fd .= pack("V", crc32($content)); // crc-32
$fd .= pack("V", strlen($compressed_data)); // Compressed size
$fd .= pack("V", strlen($content)); // Uncompressed size
$fd .= pack("v", strlen($name)); // File name length
$fd .= pack("v", 0); // Extra field length
$fd .= pack("v", 0); // File comment length
$fd .= pack("v", 0); // Disk number start
$fd .= pack("v", 0); // Internal file attributes
$fd .= pack("V", 32); // External file attributes
$fd .= pack("V", $offset); // Relative offset of local header
$fd .= $name; // File name
$fd .= ''; // Extra field (is empty)
$fd .= ''; // File comment (is empty)
/*
// Data descriptor
$fd .= pack("V", crc32($content)); // crc-32
$fd .= pack("V", strlen($compressed_data)); // Compressed size
$fd .= pack("V", strlen($content)); // Uncompressed size
*/
// Append file data to the central directory data
$cd_data[] = $fd;
}
// Digital signature
$digital_signature = '';
$digital_signature .= "\x50\x4b\x05\x05"; // Header signature
$digital_signature .= pack("v", 0); // Size of data
$digital_signature .= ''; // Signature data (is empty)
$tmp_file_data = implode('', $file_data); // File data
$tmp_cd_data = implode('', $cd_data). // Central directory
$digital_signature; // Digital signature
// End of central directory
$eof_cd = '';
$eof_cd .= "\x50\x4b\x05\x06"; // End of central dir signature
$eof_cd .= pack("v", 0); // Number of this disk
$eof_cd .= pack("v", 0); // Number of the disk with the start of the central directory
$eof_cd .= pack("v", count($cd_data)); // Total number of entries in the central directory on this disk
$eof_cd .= pack("v", count($cd_data)); // Total number of entries in the central directory
$eof_cd .= pack("V", strlen($tmp_cd_data)); // Size of the central directory
$eof_cd .= pack("V", strlen($tmp_file_data)); // Offset of start of central directory with respect to the starting disk number
$eof_cd .= pack("v", 0); // .ZIP file comment length
$eof_cd .= ''; // .ZIP file comment (is empty)
// Content of the zip file
$data = $tmp_file_data.
// $extra_data_record.
$tmp_cd_data.
$eof_cd;
// Return content?
if(!$filename)
return $data;
// Write to file
return file_put_contents($filename, $data);
}
/**
* Load a zip file
*
* This function loads the files and dirs from a zip file from the harddrive.
*
* @access public
*
* @param string $file The path to the zip file
* @param bool $reset Reset the files and dirs before adding the zip file's content?
*
* @return bool Returns true if the file was loaded sucessfully
*/
function load_file($file, $reset = true) {
// Check whether the file exists
if(!file_exists($file))
return false;
// Load the files content
$content = @file_get_contents($file);
// Return false if the file cannot be opened
if(!$content)
return false;
// Read the zip
return $this->load_string($content, $reset);
}
/**
* Load a zip string
*
* This function loads the files and dirs from a string
*
* @access public
*
* @param string $string The string the zip is generated from
* @param bool $reset Reset the files and dirs before adding the zip file's content?
*
* @return bool Returns true if the string was loaded sucessfully
*/
function load_string($string, $reset = true) {
// Reset the zip?
if($reset) {
$this->dirs = array();
$this->files = array();
}
// Get the starting position of the end of central directory record
$start = strpos($string, "\x50\x4b\x05\x06");
// Error
if($start === false)
die('Could not find the end of central directory record');
// Get the ecdr
$eof_cd = substr($string, $start+4, 18);
// Unpack the ecdr infos
$eof_cd = unpack('vdisc1/'.
'vdisc2/'.
'ventries1/'.
'ventries2/'.
'Vsize/'.
'Voffset/'.
'vcomment_lenght', $eof_cd);
// Do not allow multi disc zips
if($eof_cd['disc1'] != 0)
die('multi disk stuff is not yet implemented :/');
// Save the interesting values
$cd_entries = $eof_cd['entries1'];
$cd_size = $eof_cd['size'];
$cd_offset = $eof_cd['offset'];
// Get the central directory record
$cdr = substr($string, $cd_offset, $cd_size);
// Reset the position and the list of the entries
$pos = 0;
$entries = array();
// Handle cdr
while($pos < strlen($cdr)) {
// Check header signature
// Digital signature
if(substr($cdr, $pos, 4) == "\x50\x4b\x05\x05") {
// Get digital signature size
$tmp_info = unpack('vsize', substr($cdr, $pos + 4, 2));
// Read out the digital signature
$digital_sig = substr($header, $pos + 6, $tmp_info['size']);
break;
}
// Get file header
$header = substr($cdr, $pos, 46);
// Unpack the header information
$header_info = @unpack('Vheader/'.
'vversion_made_by/'.
'vversion_needed/'.
'vgeneral_purpose/'.
'vcompression_method/'.
'vlast_mod_time/'.
'vlast_mod_date/'.
'Vcrc32/'.
'Vcompressed_size/'.
'Vuncompressed_size/'.
'vname_length/'.
'vextra_length/'.
'vcomment_length/'.
'vdisk_number/'.
'vinternal_attributes/'.
'Vexternal_attributes/'.
'Voffset',
$header);
// Valid header?
if($header_info['header'] != 33639248)
return false;
// New position
$pos += 46;
// Read out the file name
$header_info['name'] = substr($cdr, $pos, $header_info['name_length']);
// New position
$pos += $header_info['name_length'];
// Read out the extra stuff
$header_info['extra'] = substr($cdr, $pos, $header_info['extra_length']);
// New position
$pos += $header_info['extra_length'];
// Read out the comment
$header_info['comment'] = substr($cdr, $pos, $header_info['comment_length']);
// New position
$pos += $header_info['comment_length'];
// Append this file/dir to the entry list
$entries[] = $header_info;
}
// Check whether all entries where read sucessfully
if(count($entries) != $cd_entries)
return false;
// Handle files/dirs
foreach($entries as $entry) {
// Is a dir?
if($entry['external_attributes'] & 16) {
$this->add_dir($entry['name']);
continue;
}
// Get local file header
$header = substr($string, $entry['offset'], 30);
// Unpack the header information
$header_info = @unpack('Vheader/'.
'vversion_needed/'.
'vgeneral_purpose/'.
'vcompression_method/'.
'vlast_mod_time/'.
'vlast_mod_date/'.
'Vcrc32/'.
'Vcompressed_size/'.
'Vuncompressed_size/'.
'vname_length/'.
'vextra_length',
$header);
// Valid header?
if($header_info['header'] != 67324752)
return false;
// Get content start position
$start = $entry['offset'] + 30 + $header_info['name_length'] + $header_info['extra_length'];
// Get the compressed data
$data = substr($string, $start, $header_info['compressed_size']);
// Detect compression type
switch($header_info['compression_method']) {
// No compression
case 0:
// Ne decompression needed
$content = $data;
break;
// Gzip
case 8:
if(!function_exists('gzinflate'))
return false;
// Uncompress data
$content = gzinflate($data);
break;
// Bzip2
case 12:
if(!function_exists('bzdecompress'))
return false;
// Decompress data
$content = bzdecompress($data);
break;
// Compression not supported -> error
default:
return false;
}
// Try to add file
if(!$this->add_file($entry['name'], $content))
return false;
}
return true;
}
}
function &byref() {
$x = array();
return $x;
}
?>