diff options
| author | wei <> | 2006-01-30 08:31:32 +0000 | 
|---|---|---|
| committer | wei <> | 2006-01-30 08:31:32 +0000 | 
| commit | bcf13b2569a48e96cc1aa3da05cea27758dc994d (patch) | |
| tree | 0f19bfe3123a741eac4553712d09b4b2c8800c27 | |
| parent | 984ffd32335727c8329cb15d777fbc9adc0d3052 (diff) | |
Add Tar file extractor to publish tar file assets (e.g. tiny_mce).
| -rw-r--r-- | .gitattributes | 1 | ||||
| -rw-r--r-- | framework/Data/TTarFileExtractor.php | 556 | 
2 files changed, 557 insertions, 0 deletions
| diff --git a/.gitattributes b/.gitattributes index 10ca1147..61dd7868 100644 --- a/.gitattributes +++ b/.gitattributes @@ -234,6 +234,7 @@ framework/Data/TCache.php -text  framework/Data/TDateTimeSimpleFormatter.php -text  framework/Data/TMemCache.php -text  framework/Data/TSqliteCache.php -text +framework/Data/TTarFileExtractor.php -text  framework/Data/TXmlDocument.php -text  framework/Exceptions/TErrorHandler.php -text  framework/Exceptions/TException.php -text diff --git a/framework/Data/TTarFileExtractor.php b/framework/Data/TTarFileExtractor.php new file mode 100644 index 00000000..fc748c21 --- /dev/null +++ b/framework/Data/TTarFileExtractor.php @@ -0,0 +1,556 @@ +<?php
 +/* vim: set ts=4 sw=4: */
 +// +----------------------------------------------------------------------+
 +// | PHP Version 4                                                        |
 +// +----------------------------------------------------------------------+
 +// | Copyright (c) 1997-2003 The PHP Group                                |
 +// +----------------------------------------------------------------------+
 +// | This source file is subject to version 3.0 of the PHP license,       |
 +// | that is bundled with this package in the file LICENSE, and is        |
 +// | available through the world-wide-web at the following url:           |
 +// | http://www.php.net/license/3_0.txt.                                  |
 +// | If you did not receive a copy of the PHP license and are unable to   |
 +// | obtain it through the world-wide-web, please send a note to          |
 +// | license@php.net so we can mail you a copy immediately.               |
 +// +----------------------------------------------------------------------+
 +// | Author: Vincent Blavet <vincent@phpconcept.net>                      |
 +// +----------------------------------------------------------------------+
 +//
 +// $Id: Tar.php,v 1.29 2005/03/17 21:02:31 vblavet Exp $
 +
 +
 +class TTarFileExtractor
 +{
 +    /**
 +    * @var string Name of the Tar
 +    */
 +    private $_tarname='';
 +
 +    /**
 +    * @var file descriptor
 +    */
 +    private $_file=0;
 +
 +    /**
 +    * @var string Local Tar name of a remote Tar (http:// or ftp://)
 +    */
 +    private $_temp_tarname='';
 +
 +    /**
 +    * Archive_Tar Class constructor. This flavour of the constructor only
 +    * declare a new Archive_Tar object, identifying it by the name of the
 +    * tar file.
 +    *
 +    * @param    string  $p_tarname  The name of the tar archive to create
 +    * @access public
 +    */
 +    public function __construct($p_tarname)
 +    {
 +        $this->_tarname = $p_tarname;
 +    }
 +
 +    public function __destruct()
 +    {
 +        $this->_close();
 +        // ----- Look for a local copy to delete
 +        if ($this->_temp_tarname != '')
 +            @unlink($this->_temp_tarname);
 +    }
 +
 +    public function extract($p_path='')
 +    {
 +        return $this->extractModify($p_path, '');
 +    }
 +
 +    /**
 +    * This method extract all the content of the archive in the directory
 +    * indicated by $p_path. When relevant the memorized path of the
 +    * files/dir can be modified by removing the $p_remove_path path at the
 +    * beginning of the file/dir path.
 +    * While extracting a file, if the directory path does not exists it is
 +    * created.
 +    * While extracting a file, if the file already exists it is replaced
 +    * without looking for last modification date.
 +    * While extracting a file, if the file already exists and is write
 +    * protected, the extraction is aborted.
 +    * While extracting a file, if a directory with the same name already
 +    * exists, the extraction is aborted.
 +    * While extracting a directory, if a file with the same name already
 +    * exists, the extraction is aborted.
 +    * While extracting a file/directory if the destination directory exist
 +    * and is write protected, or does not exist but can not be created,
 +    * the extraction is aborted.
 +    * If after extraction an extracted file does not show the correct
 +    * stored file size, the extraction is aborted.
 +    * When the extraction is aborted, a PEAR error text is set and false
 +    * is returned. However the result can be a partial extraction that may
 +    * need to be manually cleaned.
 +    *
 +    * @param string $p_path         The path of the directory where the
 +	*                               files/dir need to by extracted.
 +    * @param string $p_remove_path  Part of the memorized path that can be
 +	*                               removed if present at the beginning of
 +	*                               the file/dir path.
 +    * @return boolean               true on success, false on error.
 +    * @access public
 +    */
 +    protected function extractModify($p_path, $p_remove_path)
 +    {
 +        $v_result = true;
 +        $v_list_detail = array();
 +
 +        if ($v_result = $this->_openRead()) {
 +            $v_result = $this->_extractList($p_path, $v_list_detail,
 +			                                "complete", 0, $p_remove_path);
 +            $this->_close();
 +        }
 +
 +        return $v_result;
 +    }
 +
 +    protected function _error($p_message)
 +    {
 +		throw new Exception($p_message);
 +    }
 + 
 +    private function _isArchive($p_filename=NULL)
 +    {
 +        if ($p_filename == NULL) {
 +            $p_filename = $this->_tarname;
 +        }
 +        clearstatcache();
 +        return @is_file($p_filename);
 +    }
 +
 +    private function _openRead()
 +    {
 +        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
 +
 +          // ----- Look if a local copy need to be done
 +          if ($this->_temp_tarname == '') {
 +              $this->_temp_tarname = uniqid('tar').'.tmp';
 +              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
 +                $this->_error('Unable to open in read mode \''
 +				              .$this->_tarname.'\'');
 +                $this->_temp_tarname = '';
 +                return false;
 +              }
 +              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
 +                $this->_error('Unable to open in write mode \''
 +				              .$this->_temp_tarname.'\'');
 +                $this->_temp_tarname = '';
 +                return false;
 +              }
 +              while ($v_data = @fread($v_file_from, 1024))
 +                  @fwrite($v_file_to, $v_data);
 +              @fclose($v_file_from);
 +              @fclose($v_file_to);
 +          }
 +
 +          // ----- File to open if the local copy
 +          $v_filename = $this->_temp_tarname;
 +
 +        } else
 +          // ----- File to open if the normal Tar file
 +          $v_filename = $this->_tarname;
 +
 +		$this->_file = @fopen($v_filename, "rb");
 +
 +        if ($this->_file == 0) {
 +            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    private function _close()
 +    {
 +        //if (isset($this->_file)) {
 +        if (is_resource($this->_file)) 
 +		{
 +               @fclose($this->_file);
 +            $this->_file = 0;
 +        }
 +
 +        // ----- Look if a local copy need to be erase
 +        // Note that it might be interesting to keep the url for a time : ToDo
 +        if ($this->_temp_tarname != '') {
 +            @unlink($this->_temp_tarname);
 +            $this->_temp_tarname = '';
 +        }
 +
 +        return true;
 +    }
 +
 +    private function _cleanFile()
 +    {
 +        $this->_close();
 +
 +        // ----- Look for a local copy
 +        if ($this->_temp_tarname != '') {
 +            // ----- Remove the local copy but not the remote tarname
 +            @unlink($this->_temp_tarname);
 +            $this->_temp_tarname = '';
 +        } else {
 +            // ----- Remove the local tarname file
 +            @unlink($this->_tarname);
 +        }
 +        $this->_tarname = '';
 +
 +        return true;
 +    }
 +
 +    private function _readBlock()
 +    {
 +      $v_block = null;
 +      if (is_resource($this->_file)) {
 +              $v_block = @fread($this->_file, 512);
 +      }
 +      return $v_block;
 +    }
 +
 +    private function _jumpBlock($p_len=null)
 +    {
 +      if (is_resource($this->_file)) {
 +          if ($p_len === null)
 +              $p_len = 1;
 +
 +              @fseek($this->_file, @ftell($this->_file)+($p_len*512));
 +      }
 +      return true;
 +    }
 +
 +    private function _readHeader($v_binary_data, &$v_header)
 +    {
 +        if (strlen($v_binary_data)==0) {
 +            $v_header['filename'] = '';
 +            return true;
 +        }
 +
 +        if (strlen($v_binary_data) != 512) {
 +            $v_header['filename'] = '';
 +            $this->_error('Invalid block size : '.strlen($v_binary_data));
 +            return false;
 +        }
 +
 +        // ----- Calculate the checksum
 +        $v_checksum = 0;
 +        // ..... First part of the header
 +        for ($i=0; $i<148; $i++)
 +            $v_checksum+=ord(substr($v_binary_data,$i,1));
 +        // ..... Ignore the checksum value and replace it by ' ' (space)
 +        for ($i=148; $i<156; $i++)
 +            $v_checksum += ord(' ');
 +        // ..... Last part of the header
 +        for ($i=156; $i<512; $i++)
 +           $v_checksum+=ord(substr($v_binary_data,$i,1));
 +
 +        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
 +		                 ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
 +						 ."a32uname/a32gname/a8devmajor/a8devminor",
 +						 $v_binary_data);
 +
 +        // ----- Extract the checksum
 +        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
 +        if ($v_header['checksum'] != $v_checksum) {
 +            $v_header['filename'] = '';
 +
 +            // ----- Look for last block (empty block)
 +            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
 +                return true;
 +
 +            $this->_error('Invalid checksum for file "'.$v_data['filename']
 +			              .'" : '.$v_checksum.' calculated, '
 +						  .$v_header['checksum'].' expected');
 +            return false;
 +        }
 +
 +        // ----- Extract the properties
 +        $v_header['filename'] = trim($v_data['filename']);
 +        $v_header['mode'] = OctDec(trim($v_data['mode']));
 +        $v_header['uid'] = OctDec(trim($v_data['uid']));
 +        $v_header['gid'] = OctDec(trim($v_data['gid']));
 +        $v_header['size'] = OctDec(trim($v_data['size']));
 +        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
 +        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
 +          $v_header['size'] = 0;
 +        }
 +        return true;
 +    }
 +
 +    private function _readLongHeader(&$v_header)
 +    {
 +      $v_filename = '';
 +      $n = floor($v_header['size']/512);
 +      for ($i=0; $i<$n; $i++) {
 +        $v_content = $this->_readBlock();
 +        $v_filename .= $v_content;
 +      }
 +      if (($v_header['size'] % 512) != 0) {
 +        $v_content = $this->_readBlock();
 +        $v_filename .= $v_content;
 +      }
 +
 +      // ----- Read the next header
 +      $v_binary_data = $this->_readBlock();
 +
 +      if (!$this->_readHeader($v_binary_data, $v_header))
 +        return false;
 +
 +      $v_header['filename'] = $v_filename;
 +
 +      return true;
 +    }
 +
 +    protected function _extractList($p_path, &$p_list_detail, $p_mode,
 +	                      $p_file_list, $p_remove_path)
 +    {
 +    $v_result=true;
 +    $v_nb = 0;
 +    $v_extract_all = true;
 +    $v_listing = false;
 +
 +    $p_path = $this->_translateWinPath($p_path, false);
 +    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
 +	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
 +      $p_path = "./".$p_path;
 +    }
 +    $p_remove_path = $this->_translateWinPath($p_remove_path);
 +
 +    // ----- Look for path to remove format (should end by /)
 +    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
 +      $p_remove_path .= '/';
 +    $p_remove_path_size = strlen($p_remove_path);
 +
 +    switch ($p_mode) {
 +      case "complete" :
 +        $v_extract_all = TRUE;
 +        $v_listing = FALSE;
 +      break;
 +      case "partial" :
 +          $v_extract_all = FALSE;
 +          $v_listing = FALSE;
 +      break;
 +      case "list" :
 +          $v_extract_all = FALSE;
 +          $v_listing = TRUE;
 +      break;
 +      default :
 +        $this->_error('Invalid extract mode ('.$p_mode.')');
 +        return false;
 +    }
 +
 +    clearstatcache();
 +
 +    while (strlen($v_binary_data = $this->_readBlock()) != 0)
 +    {
 +      $v_extract_file = FALSE;
 +      $v_extraction_stopped = 0;
 +
 +      if (!$this->_readHeader($v_binary_data, $v_header))
 +        return false;
 +
 +      if ($v_header['filename'] == '') {
 +        continue;
 +      }
 +
 +      // ----- Look for long filename
 +      if ($v_header['typeflag'] == 'L') {
 +        if (!$this->_readLongHeader($v_header))
 +          return false;
 +      }
 +
 +      if ((!$v_extract_all) && (is_array($p_file_list))) {
 +        // ----- By default no unzip if the file is not found
 +        $v_extract_file = false;
 +
 +        for ($i=0; $i<sizeof($p_file_list); $i++) {
 +          // ----- Look if it is a directory
 +          if (substr($p_file_list[$i], -1) == '/') {
 +            // ----- Look if the directory is in the filename path
 +            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
 +			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
 +				    == $p_file_list[$i])) {
 +              $v_extract_file = TRUE;
 +              break;
 +            }
 +          }
 +
 +          // ----- It is a file, so compare the file names
 +          elseif ($p_file_list[$i] == $v_header['filename']) {
 +            $v_extract_file = TRUE;
 +            break;
 +          }
 +        }
 +      } else {
 +        $v_extract_file = TRUE;
 +      }
 +
 +      // ----- Look if this file need to be extracted
 +      if (($v_extract_file) && (!$v_listing))
 +      {
 +        if (($p_remove_path != '')
 +            && (substr($v_header['filename'], 0, $p_remove_path_size)
 +			    == $p_remove_path))
 +          $v_header['filename'] = substr($v_header['filename'],
 +		                                 $p_remove_path_size);
 +        if (($p_path != './') && ($p_path != '/')) {
 +          while (substr($p_path, -1) == '/')
 +            $p_path = substr($p_path, 0, strlen($p_path)-1);
 +
 +          if (substr($v_header['filename'], 0, 1) == '/')
 +              $v_header['filename'] = $p_path.$v_header['filename'];
 +          else
 +            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
 +        }
 +        if (file_exists($v_header['filename'])) {
 +          if (   (@is_dir($v_header['filename']))
 +		      && ($v_header['typeflag'] == '')) {
 +            $this->_error('File '.$v_header['filename']
 +			              .' already exists as a directory');
 +            return false;
 +          }
 +          if (   ($this->_isArchive($v_header['filename']))
 +		      && ($v_header['typeflag'] == "5")) {
 +            $this->_error('Directory '.$v_header['filename']
 +			              .' already exists as a file');
 +            return false;
 +          }
 +          if (!is_writeable($v_header['filename'])) {
 +            $this->_error('File '.$v_header['filename']
 +			              .' already exists and is write protected');
 +            return false;
 +          }
 +          if (filemtime($v_header['filename']) > $v_header['mtime']) {
 +            // To be completed : An error or silent no replace ?
 +          }
 +        }
 +
 +        // ----- Check the directory availability and create it if necessary
 +        elseif (($v_result
 +		         = $this->_dirCheck(($v_header['typeflag'] == "5"
 +				                    ?$v_header['filename']
 +									:dirname($v_header['filename'])))) != 1) {
 +            $this->_error('Unable to create path for '.$v_header['filename']);
 +            return false;
 +        }
 +
 +        if ($v_extract_file) {
 +          if ($v_header['typeflag'] == "5") {
 +            if (!@file_exists($v_header['filename'])) {
 +                if (!@mkdir($v_header['filename'], 0777)) {
 +                    $this->_error('Unable to create directory {'
 +					              .$v_header['filename'].'}');
 +                    return false;
 +                }
 +            }
 +          } else {
 +              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
 +                  $this->_error('Error while opening {'.$v_header['filename']
 +				                .'} in write binary mode');
 +                  return false;
 +              } else {
 +                  $n = floor($v_header['size']/512);
 +                  for ($i=0; $i<$n; $i++) {
 +                      $v_content = $this->_readBlock();
 +                      fwrite($v_dest_file, $v_content, 512);
 +                  }
 +            if (($v_header['size'] % 512) != 0) {
 +              $v_content = $this->_readBlock();
 +              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
 +            }
 +
 +            @fclose($v_dest_file);
 +
 +            // ----- Change the file mode, mtime
 +            @touch($v_header['filename'], $v_header['mtime']);
 +            // To be completed
 +            //chmod($v_header[filename], DecOct($v_header[mode]));
 +          }
 +
 +          // ----- Check the file size
 +          clearstatcache();
 +          if (filesize($v_header['filename']) != $v_header['size']) {
 +              $this->_error('Extracted file '.$v_header['filename']
 +			                .' does not have the correct file size \''
 +							.filesize($v_header['filename'])
 +							.'\' ('.$v_header['size']
 +							.' expected). Archive may be corrupted.');
 +              return false;
 +          }
 +          }
 +        } else {
 +          $this->_jumpBlock(ceil(($v_header['size']/512)));
 +        }
 +      } else {
 +          $this->_jumpBlock(ceil(($v_header['size']/512)));
 +      }
 +
 +      /* TBC : Seems to be unused ...
 +      if ($this->_compress)
 +        $v_end_of_file = @gzeof($this->_file);
 +      else
 +        $v_end_of_file = @feof($this->_file);
 +        */
 +
 +      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
 +        // ----- Log extracted files
 +        if (($v_file_dir = dirname($v_header['filename']))
 +		    == $v_header['filename'])
 +          $v_file_dir = '';
 +        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
 +          $v_file_dir = '/';
 +
 +        $p_list_detail[$v_nb++] = $v_header;
 +      }
 +    }
 +
 +        return true;
 +    }
 +
 +    /**
 +     * Check if a directory exists and create it (including parent
 +     * dirs) if not.
 +     *
 +     * @param string $p_dir directory to check
 +     *
 +     * @return bool TRUE if the directory exists or was created
 +     */
 +    protected function _dirCheck($p_dir)
 +    {
 +        if ((@is_dir($p_dir)) || ($p_dir == ''))
 +            return true;
 +
 +        $p_parent_dir = dirname($p_dir);
 +
 +        if (($p_parent_dir != $p_dir) &&
 +            ($p_parent_dir != '') &&
 +            (!$this->_dirCheck($p_parent_dir)))
 +             return false;
 +
 +        if (!@mkdir($p_dir, 0777)) {
 +            $this->_error("Unable to create directory '$p_dir'");
 +            return false;
 +        }
 +
 +        return true;
 +    }
 +
 +    protected function _translateWinPath($p_path, $p_remove_disk_letter=true)
 +    {
 +      if (substr(PHP_OS, 0, 3) == 'WIN') {
 +          // ----- Look for potential disk letter
 +          if (   ($p_remove_disk_letter)
 +		      && (($v_position = strpos($p_path, ':')) != false)) {
 +              $p_path = substr($p_path, $v_position+1);
 +          }
 +          // ----- Change potential windows directory separator
 +          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
 +              $p_path = strtr($p_path, '\\', '/');
 +          }
 +      }
 +      return $p_path;
 +    }
 +}
 +?>
 | 
