10 defined(
'JPATH_PLATFORM') or die;
12 jimport('joomla.filesystem.file');
13 jimport('joomla.filesystem.folder');
47 private $_methods = array(0x0 =>
'None', 0x1 =>
'Shrunk', 0x2 =>
'Super Fast', 0x3 =>
'Fast', 0x4 =>
'Normal', 0x5 =>
'Maximum', 0x6 =>
'Imploded',
56 private $_ctrlDirHeader =
"\x50\x4b\x01\x02";
64 private $_ctrlDirEnd =
"\x50\x4b\x05\x06\x00\x00\x00\x00";
72 private $_fileHeader =
"\x50\x4b\x03\x04";
80 private $_data = null;
88 private $_metadata = null;
102 public function create($archive, $files)
107 foreach ($files as $file)
109 $this->_addToZIPFile($file, $contents, $ctrldir);
112 return $this->_createZIPFile($contents, $ctrldir, $archive);
127 public function extract($archive, $destination, array $options = array())
129 if (!is_file($archive))
131 if (class_exists(
'JError'))
137 throw new RuntimeException(
'Archive does not exist');
141 if ($this->hasNativeSupport())
143 return $this->extractNative($archive, $destination);
147 return $this->extractCustom($archive, $destination);
158 public static function isSupported()
160 return (self::hasNativeSupport() || extension_loaded(
'zlib'));
170 public static function hasNativeSupport()
172 return (function_exists(
'zip_open') && function_exists(
'zip_read'));
184 public function checkZipData(&$data)
186 if (strpos($data, $this->_fileHeader) ===
false)
207 protected function extractCustom($archive, $destination)
210 $this->_metadata = null;
212 if (!extension_loaded(
'zlib'))
214 if (class_exists(
'JError'))
220 throw new RuntimeException(
'Zlib not supported');
224 $this->_data = file_get_contents($archive);
228 if (class_exists(
'JError'))
234 throw new RuntimeException(
'Unable to read archive (zip)');
238 if (!$this->_readZipInfo($this->_data))
240 if (class_exists(
'JError'))
246 throw new RuntimeException(
'Get ZIP Information failed');
250 for ($i = 0, $n = count($this->_metadata); $i < $n; $i++)
252 $lastPathCharacter = substr($this->_metadata[$i][
'name'], -1, 1);
254 if ($lastPathCharacter !==
'/' && $lastPathCharacter !==
'\\')
256 $buffer = $this->_getFileData($i);
257 $path =
JPath::clean($destination .
'/' . $this->_metadata[$i][
'name']);
262 if (class_exists(
'JError'))
268 throw new RuntimeException(
'Unable to create destination');
274 if (class_exists(
'JError'))
280 throw new RuntimeException(
'Unable to write entry');
300 protected function extractNative($archive, $destination)
302 $zip = zip_open($archive);
304 if (is_resource($zip))
309 if (class_exists(
'JError'))
315 throw new RuntimeException(
'Unable to create destination');
320 while ($file = @zip_read($zip))
322 if (zip_entry_open($zip, $file,
"r"))
324 if (substr(zip_entry_name($file), strlen(zip_entry_name($file)) - 1) !=
"/")
326 $buffer = zip_entry_read($file, zip_entry_filesize($file));
328 if (
JFile::write($destination .
'/' . zip_entry_name($file), $buffer) ===
false)
330 if (class_exists(
'JError'))
336 throw new RuntimeException(
'Unable to write entry');
340 zip_entry_close($file);
345 if (class_exists(
'JError'))
351 throw new RuntimeException(
'Unable to read entry');
360 if (class_exists(
'JError'))
366 throw new RuntimeException(
'Unable to open archive');
395 private function _readZipInfo(&$data)
400 $fhLast = strpos($data, $this->_ctrlDirEnd);
406 while (($fhLast = strpos($data, $this->_ctrlDirEnd, $fhLast + 1)) !==
false);
413 $endOfCentralDirectory = unpack(
414 'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' .
415 'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength',
416 substr($data, $last + 4)
418 $offset = $endOfCentralDirectory[
'CentralDirectoryOffset'];
422 $fhStart = strpos($data, $this->_ctrlDirHeader, $offset);
423 $dataLength = strlen($data);
427 if ($dataLength < $fhStart + 31)
429 if (class_exists(
'JError'))
435 throw new RuntimeException(
'Invalid Zip Data');
439 $info = unpack(
'vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20));
440 $name = substr($data, $fhStart + 46, $info[
'Length']);
442 $entries[$name] = array(
444 'crc' => sprintf(
"%08s", dechex($info[
'CRC32'])),
445 'csize' => $info[
'Compressed'],
447 '_dataStart' => null,
449 'method' => $this->_methods[$info[
'Method']],
450 '_method' => $info[
'Method'],
451 'size' => $info[
'Uncompressed'],
455 $entries[$name][
'date'] = mktime(
456 (($info[
'Time'] >> 11) & 0x1f),
457 (($info[
'Time'] >> 5) & 0x3f),
458 (($info[
'Time'] << 1) & 0x3e),
459 (($info[
'Time'] >> 21) & 0x07),
460 (($info[
'Time'] >> 16) & 0x1f),
461 ((($info[
'Time'] >> 25) & 0x7f) + 1980)
464 if ($dataLength < $fhStart + 43)
466 if (class_exists(
'JError'))
472 throw new RuntimeException(
'Invalid ZIP data');
476 $info = unpack(
'vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10));
478 $entries[$name][
'type'] = ($info[
'Internal'] & 0x01) ?
'text' :
'binary';
479 $entries[$name][
'attr'] = (($info[
'External'] & 0x10) ?
'D' :
'-') . (($info[
'External'] & 0x20) ?
'A' :
'-')
480 . (($info[
'External'] & 0x03) ?
'S' :
'-') . (($info[
'External'] & 0x02) ?
'H' :
'-') . (($info[
'External'] & 0x01) ?
'R' :
'-');
481 $entries[$name][
'offset'] = $info[
'Offset'];
484 $lfhStart = strpos($data, $this->_fileHeader, $entries[$name][
'offset']);
486 if ($dataLength < $lfhStart + 34)
488 if (class_exists(
'JError'))
494 throw new RuntimeException(
'Invalid Zip Data');
498 $info = unpack(
'vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25));
499 $name = substr($data, $lfhStart + 30, $info[
'Length']);
500 $entries[$name][
'_dataStart'] = $lfhStart + 30 + $info[
'Length'] + $info[
'ExtraLength'];
503 @set_time_limit(ini_get(
'max_execution_time'));
505 while ((($fhStart = strpos($data, $this->_ctrlDirHeader, $fhStart + 46)) !==
false));
507 $this->_metadata = array_values($entries);
521 private function _getFileData($key)
523 if ($this->_metadata[$key][
'_method'] == 0x8)
525 return gzinflate(substr($this->_data, $this->_metadata[$key][
'_dataStart'], $this->_metadata[$key][
'csize']));
527 elseif ($this->_metadata[$key][
'_method'] == 0x0)
530 return substr($this->_data, $this->_metadata[$key][
'_dataStart'], $this->_metadata[$key][
'csize']);
532 elseif ($this->_metadata[$key][
'_method'] == 0x12)
535 if (extension_loaded(
'bz2'))
537 return bzdecompress(substr($this->_data, $this->_metadata[$key][
'_dataStart'], $this->_metadata[$key][
'csize']));
555 protected function _unix2DOSTime($unixtime = null)
557 $timearray = (is_null($unixtime)) ? getdate() : getdate($unixtime);
559 if ($timearray[
'year'] < 1980)
561 $timearray[
'year'] = 1980;
562 $timearray[
'mon'] = 1;
563 $timearray[
'mday'] = 1;
564 $timearray[
'hours'] = 0;
565 $timearray[
'minutes'] = 0;
566 $timearray[
'seconds'] = 0;
569 return (($timearray[
'year'] - 1980) << 25) | ($timearray[
'mon'] << 21) | ($timearray[
'mday'] << 16) | ($timearray[
'hours'] << 11) |
570 ($timearray[
'minutes'] << 5) | ($timearray[
'seconds'] >> 1);
586 private function _addToZIPFile(array &$file, array &$contents, array &$ctrldir)
588 $data = &$file[
'data'];
589 $name = str_replace(
'\\',
'/', $file[
'name']);
594 if (isset($file[
'time']))
596 $ftime = $file[
'time'];
600 $dtime = dechex($this->_unix2DosTime($ftime));
601 $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3]))
602 . chr(hexdec($dtime[0] . $dtime[1]));
605 $fr = $this->_fileHeader;
616 $unc_len = strlen($data);
618 $zdata = gzcompress($data);
619 $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
620 $c_len = strlen($zdata);
623 $fr .= pack(
'V', $crc);
625 $fr .= pack(
'V', $c_len);
627 $fr .= pack(
'V', $unc_len);
629 $fr .= pack(
'v', strlen($name));
639 $old_offset = strlen(implode(
'', $contents));
643 $cdrec = $this->_ctrlDirHeader;
645 $cdrec .=
"\x00\x00";
647 $cdrec .=
"\x14\x00";
649 $cdrec .=
"\x00\x00";
651 $cdrec .=
"\x08\x00";
655 $cdrec .= pack(
'V', $crc);
657 $cdrec .= pack(
'V', $c_len);
659 $cdrec .= pack(
'V', $unc_len);
661 $cdrec .= pack(
'v', strlen($name));
663 $cdrec .= pack(
'v', 0);
665 $cdrec .= pack(
'v', 0);
667 $cdrec .= pack(
'v', 0);
669 $cdrec .= pack(
'v', 0);
671 $cdrec .= pack(
'V', 32);
673 $cdrec .= pack(
'V', $old_offset);
679 $ctrldir[] = &$cdrec;
697 private function _createZIPFile(array &$contents, array &$ctrlDir, $path)
699 $data = implode(
'', $contents);
700 $dir = implode(
'', $ctrlDir);
702 $buffer = $data . $dir . $this->_ctrlDirEnd .
703 pack(
'v', count($ctrlDir)) .
704 pack(
'v', count($ctrlDir)) .
705 pack(
'V', strlen($dir)) .
706 pack(
'V', strlen($data)) .