Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Joomla-Framework

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /joomla/filesystem/archive/zip.php

Documentation is available at zip.php

  1. <?php
  2. /**
  3.  * @version        $Id: archive.php 6138 2007-01-02 03:44:18Z eddiea $
  4.  * @package        Joomla.Framework
  5.  * @subpackage    FileSystem
  6.  * @copyright    Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
  7.  * @license        GNU/GPL, see LICENSE.php
  8.  *  Joomla! is free software. This version may have been modified pursuant
  9.  *  to the GNU General Public License, and as distributed it includes or
  10.  *  is derivative of works licensed under the GNU General Public License or
  11.  *  other free or open source software licenses.
  12.  *  See COPYRIGHT.php for copyright notices and details.
  13.  */
  14.  
  15. // Check to ensure this file is within the rest of the framework
  16. defined('JPATH_BASE'or die();
  17.  
  18. /**
  19.  * ZIP format adapter for the JArchive class
  20.  *
  21.  * The ZIP compression code is partially based on code from:
  22.  *   Eric Mueller <[email protected]>
  23.  *   http://www.zend.com/codex.php?id=535&single=1
  24.  *
  25.  *   Deins125 <[email protected]>
  26.  *   http://www.zend.com/codex.php?id=470&single=1
  27.  *
  28.  * The ZIP compression date code is partially based on code from
  29.  *   Peter Listiak <[email protected]>
  30.  *
  31.  * This class is inspired from and draws heavily in code and concept from the Compress package of
  32.  * The Horde Project <http://www.horde.org>
  33.  *
  34.  * @contributor  Chuck Hagenbuch <[email protected]>
  35.  * @contributor  Michael Slusarz <[email protected]>
  36.  * @contributor  Michael Cochrane <[email protected]>
  37.  *
  38.  * @author        Louis Landry <[email protected]>
  39.  * @package     Joomla.Framework
  40.  * @subpackage    FileSystem
  41.  * @since        1.5
  42.  */
  43. class JArchiveZip extends JObject
  44. {
  45.     /**
  46.      * ZIP compression methods.
  47.      * @var array 
  48.      */
  49.     var $_methods = array (
  50.         0x0 => 'None',
  51.         0x1 => 'Shrunk',
  52.         0x2 => 'Super Fast',
  53.         0x3 => 'Fast',
  54.         0x4 => 'Normal',
  55.         0x5 => 'Maximum',
  56.         0x6 => 'Imploded',
  57.         0x8 => 'Deflated'
  58.     );
  59.  
  60.     /**
  61.      * Beginning of central directory record.
  62.      * @var string 
  63.      */
  64.     var $_ctrlDirHeader = "\x50\x4b\x01\x02";
  65.  
  66.     /**
  67.      * End of central directory record.
  68.      * @var string 
  69.      */
  70.     var $_ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00";
  71.  
  72.     /**
  73.      * Beginning of file contents.
  74.      * @var string 
  75.      */
  76.     var $_fileHeader = "\x50\x4b\x03\x04";
  77.  
  78.     /**
  79.      * ZIP file data buffer
  80.      * @var string 
  81.      */
  82.     var $_data = null;
  83.  
  84.     /**
  85.      * ZIP file metadata array
  86.      * @var array 
  87.      */
  88.     var $_metadata = null;
  89.  
  90.     /**
  91.      * Create a ZIP compressed file from an array of file data.
  92.      *
  93.      * @todo    Finish Implementation
  94.      *
  95.      * @access    public
  96.      * @param    string    $archive    Path to save archive
  97.      * @param    array    $files        Array of files to add to archive
  98.      * @param    array    $options    Compression options [unused]
  99.      * @return    boolean    True if successful
  100.      * @since    1.5
  101.      */
  102.     function create($archive$files$options array ())
  103.     {
  104.         // Initialize variables
  105.         $contents array();
  106.         $ctrldir  array();
  107.  
  108.         foreach ($files as $file)
  109.         {
  110.             $this->_addToZIPFile($file$contents$ctrldir);
  111.         }
  112.         return $this->_createZIPFile($contents$ctrldir);
  113.     }
  114.  
  115.     /**
  116.      * Extract a ZIP compressed file to a given path
  117.      *
  118.      * @access    public
  119.      * @param    string    $archive        Path to ZIP archive to extract
  120.      * @param    string    $destination    Path to extract archive into
  121.      * @param    array    $options        Extraction options [unused]
  122.      * @return    boolean    True if successful
  123.      * @since    1.5
  124.      */
  125.     function extract($archive$destination$options array ())
  126.     {
  127.         if ($this->hasNativeSupport()) {
  128.             return ($this->_extractNative($archive$destination$options))true JError::raiseWarning(100$this->get('error.message'));
  129.         else {
  130.             return ($this->_extract($archive$destination$options))true JError::raiseWarning(100$this->get('error.message'));
  131.         }
  132.     }
  133.  
  134.     /**
  135.      * Method to determine if the server has native zip support for faster handling
  136.      *
  137.      * @access    public
  138.      * @return    boolean    True if php has native ZIP support
  139.      * @since    1.5
  140.      */
  141.     function hasNativeSupport()
  142.     {
  143.         return (function_exists('zip_open'&& function_exists('zip_read'));
  144.     }
  145.  
  146.     /**
  147.      * Checks to see if the data is a valid ZIP file.
  148.      *
  149.      * @access    public
  150.      * @param    string    $data    ZIP archive data buffer
  151.      * @return    boolean    True if valid, false if invalid.
  152.      * @since    1.5
  153.      */
  154.     function checkZipData($data{
  155.         if (strpos($data$this->_fileHeader=== false{
  156.             return false;
  157.         else {
  158.             return true;
  159.         }
  160.     }
  161.  
  162.     /**
  163.      * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support
  164.      *
  165.      * @access    private
  166.      * @param    string    $archive        Path to ZIP archive to extract
  167.      * @param    string    $destination    Path to extract archive into
  168.      * @param    array    $options        Extraction options [unused]
  169.      * @return    boolean    True if successful
  170.      * @since    1.5
  171.      */
  172.     function _extract($archive$destination$options)
  173.     {
  174.         // Initialize variables
  175.         $this->_data = null;
  176.         $this->_metadata = null;
  177.  
  178.         if (!extension_loaded('zlib')) {
  179.             $this->set('error.message''Zlib Not Supported');
  180.             return false;
  181.         }
  182.  
  183.         if (!$this->_data = JFile::read($archive)) {
  184.             $this->set('error.message''Unable to read archive');
  185.             return false;
  186.         }
  187.         if (!$this->_getZipInfo($this->_data)) {
  188.             return false;
  189.         }
  190.  
  191.         for ($i=0,$n=count($this->_metadata);$i<$n;$i++{
  192.             if (substr($this->_metadata[$i]['name']-11!= '/' && substr($this->_metadata[$i]['name']-11!= '\\'{
  193.                 $buffer $this->_getFileData($i);
  194.                 $path JPath::clean($destination.DS.$this->_metadata[$i]['name']);
  195.                 // Make sure the destination folder exists
  196.                 if (!JFolder::create(dirname($path))) {
  197.                     $this->set('error.message''Unable to create destination');
  198.                     return false;
  199.                 }
  200.                 if (JFile::write($path$buffer=== false{
  201.                     $this->set('error.message''Unable to write entry');
  202.                     return false;
  203.                 }
  204.             }
  205.         }
  206.         return true;
  207.     }
  208.  
  209.     /**
  210.      * Extract a ZIP compressed file to a given path using native php api calls for speed
  211.      *
  212.      * @access    private
  213.      * @param    string    $archive        Path to ZIP archive to extract
  214.      * @param    string    $destination    Path to extract archive into
  215.      * @param    array    $options        Extraction options [unused]
  216.      * @return    boolean    True if successful
  217.      * @since    1.5
  218.      */
  219.     function _extractNative($archive$destination$options)
  220.     {
  221.         if ($zip zip_open($archive)) {
  222.             if ($zip{
  223.                 // Make sure the destination folder exists
  224.                 if (!JFolder::create($destination)) {
  225.                     $this->set('error.message''Unable to create destination');
  226.                     return false;
  227.                 }
  228.                 // Read files in the archive
  229.                 while ($file zip_read($zip))
  230.                 {
  231.                     if (zip_entry_open($zip$file"r")) {
  232.                         if (substr(zip_entry_name($file)strlen(zip_entry_name($file)) 1!= "/"{
  233.                             $buffer zip_entry_read($filezip_entry_filesize($file));
  234.                             if (JFile::write($destination.DS.zip_entry_name($file)$buffer=== false{
  235.                                 $this->set('error.message''Unable to write entry');
  236.                                 return false;
  237.                             }
  238.                             zip_entry_close($file);
  239.                         }
  240.                     else {
  241.                         $this->set('error.message''Unable to read entry');
  242.                         return false;
  243.                     }
  244.                 }
  245.                 zip_close($zip);
  246.             }
  247.         else {
  248.             $this->set('error.message''Unable to open archive');
  249.             return false;
  250.         }
  251.         return true;
  252.     }
  253.  
  254.     /**
  255.      * Get the list of files/data from a ZIP archive buffer.
  256.      *
  257.      * @access    private
  258.      * @param     string    $data    The ZIP archive buffer.
  259.      * @return    array    Archive metadata array
  260.      *  <pre>
  261.      *  KEY: Position in zipfile
  262.      *  VALUES: 'attr'    --  File attributes
  263.      *          'crc'     --  CRC checksum
  264.      *          'csize'   --  Compressed file size
  265.      *          'date'    --  File modification time
  266.      *          'name'    --  Filename
  267.      *          'method'  --  Compression method
  268.      *          'size'    --  Original file size
  269.      *          'type'    --  File type
  270.      *  </pre>
  271.      * @since    1.5
  272.      */
  273.     function _getZipInfo($data)
  274.     {
  275.         // Initialize variables
  276.         $entries array ();
  277.  
  278.         // Get details from Central directory structure.
  279.         $fhStart strpos($data$this->_ctrlDirHeader);
  280.         do {
  281.             if (strlen($data$fhStart +31{
  282.                 $this->set('error.message''Invalid ZIP data');
  283.                 return false;
  284.             }
  285.             $info unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength'substr($data$fhStart +1020));
  286.             $name substr($data$fhStart +46$info['Length']);
  287.  
  288.             $entries[$namearray('attr' => null'crc' => sprintf("%08s"dechex($info['CRC32'))'csize' => $info['Compressed']'date' => null'_dataStart' => null'name' => $name'method' => $this->_methods[$info['Method']]'_method' => $info['Method']'size' => $info['Uncompressed']'type' => null);
  289.             $entries[$name]['date'mktime((($info['Time'>> 110x1f)(($info['Time'>> 50x3f)(($info['Time'<< 10x3e)(($info['Time'>> 210x07)(($info['Time'>> 160x1f)((($info['Time'>> 250x7f1980));
  290.  
  291.             if (strlen($data$fhStart +43{
  292.                 $this->set('error.message''Invalid ZIP data');
  293.                 return false;
  294.             }
  295.             $info unpack('vInternal/VExternal'substr($data$fhStart +366));
  296.  
  297.             $entries[$name]['type'($info['Internal'0x01'text' 'binary';
  298.             $entries[$name]['attr'(($info['External'0x10'D' '-'.
  299.                                       (($info['External'0x20'A' '-'.
  300.                                       (($info['External'0x03'S' '-'.
  301.                                       (($info['External'0x02'H' '-'.
  302.                                       (($info['External'0x01'R' '-');
  303.         while (($fhStart strpos($data$this->_ctrlDirHeader$fhStart +46)) !== false);
  304.  
  305.         // Get details from local file header.
  306.         $fhStart strpos($data$this->_fileHeader);
  307.         do {
  308.             if (strlen($data$fhStart +34{
  309.                 $this->set('error.message''Invalid ZIP data');
  310.                 return false;
  311.             }
  312.             $info unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength'substr($data$fhStart +825));
  313.             $name substr($data$fhStart +30$info['Length']);
  314.             $entries[$name]['_dataStart'$fhStart +30 $info['Length'$info['ExtraLength'];
  315.         while (strlen($data$fhStart +30 $info['Length'&& ($fhStart strpos($data$this->_fileHeader$fhStart +30 $info['Length'])) !== false);
  316.  
  317.         $this->_metadata = array_values($entries);
  318.         return true;
  319.     }
  320.  
  321.     /**
  322.      * Returns the file data for a file by offsest in the ZIP archive
  323.      *
  324.      * @access    private
  325.      * @param    int        $key    The position of the file in the archive.
  326.      * @return    string    Uncompresed file data buffer
  327.      * @since    1.5
  328.      */
  329.     function _getFileData($key{
  330.         if ($this->_metadata[$key]['_method'== 0x8{
  331.             // If zlib extention is loaded use it
  332.             if (extension_loaded('zlib')) {
  333.                 return gzinflate(substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']));
  334.             }
  335.         }
  336.         elseif ($this->_metadata[$key]['_method'== 0x0{
  337.             /* Files that aren't compressed. */
  338.             return substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']);
  339.         elseif ($this->_metadata[$key]['_method'== 0x12{
  340.             // Is bz2 extension loaded?  If not try to load it
  341.             if (!extension_loaded('bz2')) {
  342.                 if (JPATH_ISWIN{
  343.                     dl('php_bz2.dll');
  344.                 else {
  345.                     dl('bz2.so');
  346.                 }
  347.             }
  348.             // If bz2 extention is sucessfully loaded use it
  349.             if (extension_loaded('bz2')) {
  350.                 return bzdecompress(substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']));
  351.             }
  352.         }
  353.         return '';
  354.     }
  355.  
  356.     /**
  357.      * Converts a UNIX timestamp to a 4-byte DOS date and time format
  358.      * (date in high 2-bytes, time in low 2-bytes allowing magnitude
  359.      * comparison).
  360.      *
  361.      * @access    private
  362.      * @param    int    $unixtime    The current UNIX timestamp.
  363.      * @return    int    The current date in a 4-byte DOS format.
  364.      * @since    1.5
  365.      */
  366.     function _unix2DOSTime($unixtime null{
  367.         $timearray (is_null($unixtime)) getdate(getdate($unixtime);
  368.  
  369.         if ($timearray['year'1980{
  370.             $timearray['year'1980;
  371.             $timearray['mon'1;
  372.             $timearray['mday'1;
  373.             $timearray['hours'0;
  374.             $timearray['minutes'0;
  375.             $timearray['seconds'0;
  376.         }
  377.         return (($timearray['year'1980<< 25($timearray['mon'<< 21($timearray['mday'<< 16($timearray['hours'<< 11($timearray['minutes'<< 5($timearray['seconds'>> 1);
  378.     }
  379.  
  380.     /**
  381.      * Adds a "file" to the ZIP archive.
  382.      *
  383.      * @todo Review and finish implementation
  384.      *
  385.      * @access    private
  386.      * @param    array    $file        File data array to add
  387.      * @param    array    $contents    An array of existing zipped files.
  388.      * @param    array    $ctrldir    An array of central directory information.
  389.      * @return    void 
  390.      * @since    1.5
  391.      */
  392.     function _addToZIPFile($file$contents$ctrldir{
  393.         $data $file['data'];
  394.         $name str_replace('\\''/'$file['name']);
  395.  
  396.         /* See if time/date information has been provided. */
  397.         $ftime null;
  398.         if (isset ($file['time'])) {
  399.             $ftime $file['time'];
  400.         }
  401.  
  402.         /* Get the hex time. */
  403.         $dtime dechex($this->_unix2DosTime($ftime));
  404.         $hexdtime chr(hexdec($dtime[6$dtime[7])) .
  405.         chr(hexdec($dtime[4$dtime[5])) .
  406.         chr(hexdec($dtime[2$dtime[3])) .
  407.         chr(hexdec($dtime[0$dtime[1]));
  408.  
  409.         $fr $this->_fileHeader/* Begin creating the ZIP data. */
  410.         $fr .= "\x14\x00"/* Version needed to extract. */
  411.         $fr .= "\x00\x00"/* General purpose bit flag. */
  412.         $fr .= "\x08\x00"/* Compression method. */
  413.         $fr .= $hexdtime/* Last modification time/date. */
  414.  
  415.         /* "Local file header" segment. */
  416.         $unc_len strlen($data);
  417.         $crc crc32($data);
  418.         $zdata gzcompress($data);
  419.         $zdata substr(substr($zdata0strlen($zdata4)2);
  420.         $c_len strlen($zdata);
  421.  
  422.         $fr .= pack('V'$crc)/* CRC 32 information. */
  423.         $fr .= pack('V'$c_len)/* Compressed filesize. */
  424.         $fr .= pack('V'$unc_len)/* Uncompressed filesize. */
  425.         $fr .= pack('v'strlen($name))/* Length of filename. */
  426.         $fr .= pack('v'0)/* Extra field length. */
  427.         $fr .= $name/* File name. */
  428.  
  429.         /* "File data" segment. */
  430.         $fr .= $zdata;
  431.  
  432.         /* Add this entry to array. */
  433.         $old_offset strlen(implode(''$contents));
  434.         $contents[$fr;
  435.  
  436.         /* Add to central directory record. */
  437.         $cdrec $this->_ctrlDirHeader;
  438.         $cdrec .= "\x00\x00"/* Version made by. */
  439.         $cdrec .= "\x14\x00"/* Version needed to extract */
  440.         $cdrec .= "\x00\x00"/* General purpose bit flag */
  441.         $cdrec .= "\x08\x00"/* Compression method */
  442.         $cdrec .= $hexdtime/* Last mod time/date. */
  443.         $cdrec .= pack('V'$crc)/* CRC 32 information. */
  444.         $cdrec .= pack('V'$c_len)/* Compressed filesize. */
  445.         $cdrec .= pack('V'$unc_len)/* Uncompressed filesize. */
  446.         $cdrec .= pack('v'strlen($name))/* Length of filename. */
  447.         $cdrec .= pack('v'0)/* Extra field length. */
  448.         $cdrec .= pack('v'0)/* File comment length. */
  449.         $cdrec .= pack('v'0)/* Disk number start. */
  450.         $cdrec .= pack('v'0)/* Internal file attributes. */
  451.         $cdrec .= pack('V'32)/* External file attributes -
  452.                                            'archive' bit set. */
  453.         $cdrec .= pack('V'$old_offset)/* Relative offset of local
  454.                                                     header. */
  455.         $cdrec .= $name/* File name. */
  456.         /* Optional extra field, file comment goes here. */
  457.  
  458.         // Save to central directory array. */
  459.         $ctrldir[$cdrec;
  460.     }
  461.  
  462.     /**
  463.      * Creates the ZIP file.
  464.      * Official ZIP file format: http://www.pkware.com/appnote.txt
  465.      *
  466.      * @todo Review and finish implementation
  467.      *
  468.      * @access    private
  469.      * @param    array    $contents    An array of existing zipped files.
  470.      * @param    array    $ctrldir    An array of central directory information.
  471.      * @param    string    $path        The path to store the archive.
  472.      * @return    boolean    True if successful
  473.      * @since    1.5
  474.      */
  475.     function _createZIPFile($contents$ctrlDir$path)
  476.     {
  477.         $data implode(''$contents);
  478.         $dir implode(''$ctrlDir);
  479.  
  480.         $buffer $data $dir $this->_ctrlDirEnd .
  481.         /* Total # of entries "on this disk". */
  482.         pack('v'count($ctrlDir)) .
  483.         /* Total # of entries overall. */
  484.         pack('v'count($ctrlDir)) .
  485.         /* Size of central directory. */
  486.         pack('V'strlen($dir)) .
  487.         /* Offset to start of central dir. */
  488.         pack('V'strlen($data)) .
  489.         /* ZIP file comment length. */
  490.         "\x00\x00";
  491.  
  492.         if (JFile::write($path$buffer=== false{
  493.             return false;
  494.         else {
  495.             return true;
  496.         }
  497.     }
  498. }
  499. ?>

Documentation generated on Mon, 05 Mar 2007 21:33:37 +0000 by phpDocumentor 1.3.1