Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Joomla-Framework

License

Content on this site is copyright © 2005 - 2008 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5. Some parts of this website may be subject to other licenses.
Source code for file /joomla/filesystem/archive/zip.php

Documentation is available at zip.php

  1. <?php
  2. /**
  3.  * @version        $Id:zip.php 6961 2007-03-15 16:06:53Z tcp $
  4.  * @package        Joomla.Framework
  5.  * @subpackage    FileSystem
  6.  * @copyright    Copyright (C) 2005 - 2008 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 <eric@themepark.com>
  23.  *   http://www.zend.com/codex.php?id=535&single=1
  24.  *
  25.  *   Deins125 <webmaster@atlant.ru>
  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 <mlady@users.sourceforge.net>
  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 <chuck@horde.org>
  35.  * @contributor  Michael Slusarz <slusarz@horde.org>
  36.  * @contributor  Michael Cochrane <mike@graftonhall.co.nz>
  37.  *
  38.  * @author        Louis Landry <louis.landry@joomla.org>
  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$archive);
  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 is_file($archive) )
  128.         {
  129.             $this->set('error.message''Archive does not exist');
  130.             return false;
  131.         }
  132.  
  133.         if ($this->hasNativeSupport()) {
  134.             return ($this->_extractNative($archive$destination$options))true JError::raiseWarning(100$this->get('error.message'));
  135.         else {
  136.             return ($this->_extract($archive$destination$options))true JError::raiseWarning(100$this->get('error.message'));
  137.         }
  138.     }
  139.  
  140.     /**
  141.      * Method to determine if the server has native zip support for faster handling
  142.      *
  143.      * @access    public
  144.      * @return    boolean    True if php has native ZIP support
  145.      * @since    1.5
  146.      */
  147.     function hasNativeSupport()
  148.     {
  149.         return (function_exists('zip_open'&& function_exists('zip_read'));
  150.     }
  151.  
  152.     /**
  153.      * Checks to see if the data is a valid ZIP file.
  154.      *
  155.      * @access    public
  156.      * @param    string    $data    ZIP archive data buffer
  157.      * @return    boolean    True if valid, false if invalid.
  158.      * @since    1.5
  159.      */
  160.     function checkZipData($data{
  161.         if (strpos($data$this->_fileHeader=== false{
  162.             return false;
  163.         else {
  164.             return true;
  165.         }
  166.     }
  167.  
  168.     /**
  169.      * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support
  170.      *
  171.      * @access    private
  172.      * @param    string    $archive        Path to ZIP archive to extract
  173.      * @param    string    $destination    Path to extract archive into
  174.      * @param    array    $options        Extraction options [unused]
  175.      * @return    boolean    True if successful
  176.      * @since    1.5
  177.      */
  178.     function _extract($archive$destination$options)
  179.     {
  180.         // Initialize variables
  181.         $this->_data = null;
  182.         $this->_metadata = null;
  183.  
  184.         if (!extension_loaded('zlib')) {
  185.             $this->set('error.message''Zlib Not Supported');
  186.             return false;
  187.         }
  188.  
  189.         if (!$this->_data = JFile::read($archive)) {
  190.             $this->set('error.message''Unable to read archive');
  191.             return false;
  192.         }
  193.         if (!$this->_getZipInfo($this->_data)) {
  194.             return false;
  195.         }
  196.  
  197.         for ($i=0,$n=count($this->_metadata);$i<$n;$i++{
  198.             if (substr($this->_metadata[$i]['name']-11!= '/' && substr($this->_metadata[$i]['name']-11!= '\\'{
  199.                 $buffer $this->_getFileData($i);
  200.                 $path JPath::clean($destination.DS.$this->_metadata[$i]['name']);
  201.                 // Make sure the destination folder exists
  202.                 if (!JFolder::create(dirname($path))) {
  203.                     $this->set('error.message''Unable to create destination');
  204.                     return false;
  205.                 }
  206.                 if (JFile::write($path$buffer=== false{
  207.                     $this->set('error.message''Unable to write entry');
  208.                     return false;
  209.                 }
  210.             }
  211.         }
  212.         return true;
  213.     }
  214.  
  215.     /**
  216.      * Extract a ZIP compressed file to a given path using native php api calls for speed
  217.      *
  218.      * @access    private
  219.      * @param    string    $archive        Path to ZIP archive to extract
  220.      * @param    string    $destination    Path to extract archive into
  221.      * @param    array    $options        Extraction options [unused]
  222.      * @return    boolean    True if successful
  223.      * @since    1.5
  224.      */
  225.     function _extractNative($archive$destination$options)
  226.     {
  227.         if ($zip zip_open($archive)) {
  228.             if ($zip{
  229.                 // Make sure the destination folder exists
  230.                 if (!JFolder::create($destination)) {
  231.                     $this->set('error.message''Unable to create destination');
  232.                     return false;
  233.                 }
  234.                 // Read files in the archive
  235.                 while ($file zip_read($zip))
  236.                 {
  237.                     if (zip_entry_open($zip$file"r")) {
  238.                         if (substr(zip_entry_name($file)strlen(zip_entry_name($file)) 1!= "/"{
  239.                             $buffer zip_entry_read($filezip_entry_filesize($file));
  240.                             if (JFile::write($destination.DS.zip_entry_name($file)$buffer=== false{
  241.                                 $this->set('error.message''Unable to write entry');
  242.                                 return false;
  243.                             }
  244.                             zip_entry_close($file);
  245.                         }
  246.                     else {
  247.                         $this->set('error.message''Unable to read entry');
  248.                         return false;
  249.                     }
  250.                 }
  251.                 zip_close($zip);
  252.             }
  253.         else {
  254.             $this->set('error.message''Unable to open archive');
  255.             return false;
  256.         }
  257.         return true;
  258.     }
  259.  
  260.     /**
  261.      * Get the list of files/data from a ZIP archive buffer.
  262.      *
  263.      * @access    private
  264.      * @param     string    $data    The ZIP archive buffer.
  265.      * @return    array    Archive metadata array
  266.      *  <pre>
  267.      *  KEY: Position in zipfile
  268.      *  VALUES: 'attr'    --  File attributes
  269.      *          'crc'     --  CRC checksum
  270.      *          'csize'   --  Compressed file size
  271.      *          'date'    --  File modification time
  272.      *          'name'    --  Filename
  273.      *          'method'  --  Compression method
  274.      *          'size'    --  Original file size
  275.      *          'type'    --  File type
  276.      *  </pre>
  277.      * @since    1.5
  278.      */
  279.     function _getZipInfo($data)
  280.     {
  281.         // Initialize variables
  282.         $entries array ();
  283.  
  284.         // Get details from Central directory structure.
  285.         $fhStart strpos($data$this->_ctrlDirHeader);
  286.         do {
  287.             if (strlen($data$fhStart +31{
  288.                 $this->set('error.message''Invalid ZIP data');
  289.                 return false;
  290.             }
  291.             $info unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength'substr($data$fhStart +1020));
  292.             $name substr($data$fhStart +46$info['Length']);
  293.  
  294.             $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);
  295.             $entries[$name]['date'mktime((($info['Time'>> 110x1f)(($info['Time'>> 50x3f)(($info['Time'<< 10x3e)(($info['Time'>> 210x07)(($info['Time'>> 160x1f)((($info['Time'>> 250x7f1980));
  296.  
  297.             if (strlen($data$fhStart +43{
  298.                 $this->set('error.message''Invalid ZIP data');
  299.                 return false;
  300.             }
  301.             $info unpack('vInternal/VExternal'substr($data$fhStart +366));
  302.  
  303.             $entries[$name]['type'($info['Internal'0x01'text' 'binary';
  304.             $entries[$name]['attr'(($info['External'0x10'D' '-'.
  305.                                       (($info['External'0x20'A' '-'.
  306.                                       (($info['External'0x03'S' '-'.
  307.                                       (($info['External'0x02'H' '-'.
  308.                                       (($info['External'0x01'R' '-');
  309.         while (($fhStart strpos($data$this->_ctrlDirHeader$fhStart +46)) !== false);
  310.  
  311.         // Get details from local file header.
  312.         $fhStart strpos($data$this->_fileHeader);
  313.         do {
  314.             if (strlen($data$fhStart +34{
  315.                 $this->set('error.message''Invalid ZIP data');
  316.                 return false;
  317.             }
  318.             $info unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength'substr($data$fhStart +825));
  319.             $name substr($data$fhStart +30$info['Length']);
  320.             $entries[$name]['_dataStart'$fhStart +30 $info['Length'$info['ExtraLength'];
  321.         while (strlen($data$fhStart +30 $info['Length'&& ($fhStart strpos($data$this->_fileHeader$fhStart +30 $info['Length'])) !== false);
  322.  
  323.         $this->_metadata = array_values($entries);
  324.         return true;
  325.     }
  326.  
  327.     /**
  328.      * Returns the file data for a file by offsest in the ZIP archive
  329.      *
  330.      * @access    private
  331.      * @param    int        $key    The position of the file in the archive.
  332.      * @return    string    Uncompresed file data buffer
  333.      * @since    1.5
  334.      */
  335.     function _getFileData($key{
  336.         if ($this->_metadata[$key]['_method'== 0x8{
  337.             // If zlib extention is loaded use it
  338.             if (extension_loaded('zlib')) {
  339.                 return gzinflate(substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']));
  340.             }
  341.         }
  342.         elseif ($this->_metadata[$key]['_method'== 0x0{
  343.             /* Files that aren't compressed. */
  344.             return substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']);
  345.         elseif ($this->_metadata[$key]['_method'== 0x12{
  346.             // Is bz2 extension loaded?  If not try to load it
  347.             if (!extension_loaded('bz2')) {
  348.                 if (JPATH_ISWIN{
  349.                     dl('php_bz2.dll');
  350.                 else {
  351.                     dl('bz2.so');
  352.                 }
  353.             }
  354.             // If bz2 extention is sucessfully loaded use it
  355.             if (extension_loaded('bz2')) {
  356.                 return bzdecompress(substr($this->_data$this->_metadata[$key]['_dataStart']$this->_metadata[$key]['csize']));
  357.             }
  358.         }
  359.         return '';
  360.     }
  361.  
  362.     /**
  363.      * Converts a UNIX timestamp to a 4-byte DOS date and time format
  364.      * (date in high 2-bytes, time in low 2-bytes allowing magnitude
  365.      * comparison).
  366.      *
  367.      * @access    private
  368.      * @param    int    $unixtime    The current UNIX timestamp.
  369.      * @return    int    The current date in a 4-byte DOS format.
  370.      * @since    1.5
  371.      */
  372.     function _unix2DOSTime($unixtime null{
  373.         $timearray (is_null($unixtime)) getdate(getdate($unixtime);
  374.  
  375.         if ($timearray['year'1980{
  376.             $timearray['year'1980;
  377.             $timearray['mon'1;
  378.             $timearray['mday'1;
  379.             $timearray['hours'0;
  380.             $timearray['minutes'0;
  381.             $timearray['seconds'0;
  382.         }
  383.         return