![]() ![]() |
websvn |
Subversion Repositories: |
Compare with Previous - Blame - Download
<?php/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: *//*** File::CSV** PHP versions 4 and 5** Copyright (c) 1997-2008,* Vincent Blavet <vincent@phpconcept.net>* All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:** * Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* * Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*** @category File_Formats* @package Archive_Tar* @author Vincent Blavet <vincent@phpconcept.net>* @copyright 1997-2008 The Authors* @license http://www.opensource.org/licenses/bsd-license.php New BSD License* @version CVS: $Id: Tar.php,v 1.43 2008/10/30 17:58:42 dufuz Exp $* @link http://pear.php.net/package/Archive_Tar*/require_once 'PEAR.php';define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));/*** Creates a (compressed) Tar archive** @author Vincent Blavet <vincent@phpconcept.net>* @version $Revision: 1.43 $* @license http://www.opensource.org/licenses/bsd-license.php New BSD License* @package Archive_Tar*/class Archive_Tar extends PEAR{/*** @var string Name of the Tar*/var $_tarname='';/*** @var boolean if true, the Tar file will be gzipped*/var $_compress=false;/*** @var string Type of compression : 'none', 'gz' or 'bz2'*/var $_compress_type='none';/*** @var string Explode separator*/var $_separator=' ';/*** @var file descriptor*/var $_file=0;/*** @var string Local Tar name of a remote Tar (http:// or ftp://)*/var $_temp_tarname='';// {{{ constructor/*** 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.* If the compress argument is set the tar will be read or created as a* gzip or bz2 compressed TAR file.** @param string $p_tarname The name of the tar archive to create* @param string $p_compress can be null, 'gz' or 'bz2'. This* parameter indicates if gzip or bz2 compression* is required. For compatibility reason the* boolean value 'true' means 'gz'.* @access public*/function Archive_Tar($p_tarname, $p_compress = null){$this->PEAR();$this->_compress = false;$this->_compress_type = 'none';if (($p_compress === null) || ($p_compress == '')) {if (@file_exists($p_tarname)) {if ($fp = @fopen($p_tarname, "rb")) {// look for gzip magic cookie$data = fread($fp, 2);fclose($fp);if ($data == "\37\213") {$this->_compress = true;$this->_compress_type = 'gz';// No sure it's enought for a magic code ....} elseif ($data == "BZ") {$this->_compress = true;$this->_compress_type = 'bz2';}}} else {// probably a remote file or some file accessible// through a stream interfaceif (substr($p_tarname, -2) == 'gz') {$this->_compress = true;$this->_compress_type = 'gz';} elseif ((substr($p_tarname, -3) == 'bz2') ||(substr($p_tarname, -2) == 'bz')) {$this->_compress = true;$this->_compress_type = 'bz2';}}} else {if (($p_compress === true) || ($p_compress == 'gz')) {$this->_compress = true;$this->_compress_type = 'gz';} else if ($p_compress == 'bz2') {$this->_compress = true;$this->_compress_type = 'bz2';} else {die("Unsupported compression type '$p_compress'\n"."Supported types are 'gz' and 'bz2'.\n");return false;}}$this->_tarname = $p_tarname;if ($this->_compress) { // assert zlib or bz2 extension supportif ($this->_compress_type == 'gz')$extname = 'zlib';else if ($this->_compress_type == 'bz2')$extname = 'bz2';if (!extension_loaded($extname)) {PEAR::loadExtension($extname);}if (!extension_loaded($extname)) {die("The extension '$extname' couldn't be found.\n"."Please make sure your version of PHP was built "."with '$extname' support.\n");return false;}}}// }}}// {{{ destructorfunction _Archive_Tar(){$this->_close();// ----- Look for a local copy to deleteif ($this->_temp_tarname != '')@unlink($this->_temp_tarname);$this->_PEAR();}// }}}// {{{ create()/*** This method creates the archive file and add the files / directories* that are listed in $p_filelist.* If a file with the same name exist and is writable, it is replaced* by the new tar.* The method return false and a PEAR error text.* The $p_filelist parameter can be an array of string, each string* representing a filename or a directory name with their path if* needed. It can also be a single string with names separated by a* single blank.* For each directory added in the archive, the files and* sub-directories are also added.* See also createModify() method for more details.** @param array $p_filelist An array of filenames and directory names, or a* single string with names separated by a single* blank space.* @return true on success, false on error.* @see createModify()* @access public*/function create($p_filelist){return $this->createModify($p_filelist, '', '');}// }}}// {{{ add()/*** This method add the files / directories that are listed in $p_filelist in* the archive. If the archive does not exist it is created.* The method return false and a PEAR error text.* The files and directories listed are only added at the end of the archive,* even if a file with the same name is already archived.* See also createModify() method for more details.** @param array $p_filelist An array of filenames and directory names, or a* single string with names separated by a single* blank space.* @return true on success, false on error.* @see createModify()* @access public*/function add($p_filelist){return $this->addModify($p_filelist, '', '');}// }}}// {{{ extract()function extract($p_path=''){return $this->extractModify($p_path, '');}// }}}// {{{ listContent()function listContent(){$v_list_detail = array();if ($this->_openRead()) {if (!$this->_extractList('', $v_list_detail, "list", '', '')) {unset($v_list_detail);$v_list_detail = 0;}$this->_close();}return $v_list_detail;}// }}}// {{{ createModify()/*** This method creates the archive file and add the files / directories* that are listed in $p_filelist.* If the file already exists and is writable, it is replaced by the* new tar. It is a create and not an add. If the file exists and is* read-only or is a directory it is not replaced. The method return* false and a PEAR error text.* The $p_filelist parameter can be an array of string, each string* representing a filename or a directory name with their path if* needed. It can also be a single string with names separated by a* single blank.* The path indicated in $p_remove_dir will be removed from the* memorized path of each file / directory listed when this path* exists. By default nothing is removed (empty path '')* The path indicated in $p_add_dir will be added at the beginning of* the memorized path of each file / directory listed. However it can* be set to empty ''. The adding of a path is done after the removing* of path.* The path add/remove ability enables the user to prepare an archive* for extraction in a different path than the origin files are.* See also addModify() method for file adding properties.** @param array $p_filelist An array of filenames and directory names,* or a single string with names separated by* a single blank space.* @param string $p_add_dir A string which contains a path to be added* to the memorized path of each element in* the list.* @param string $p_remove_dir A string which contains a path to be* removed from the memorized path of each* element in the list, when relevant.* @return boolean true on success, false on error.* @access public* @see addModify()*/function createModify($p_filelist, $p_add_dir, $p_remove_dir=''){$v_result = true;if (!$this->_openWrite())return false;if ($p_filelist != '') {if (is_array($p_filelist))$v_list = $p_filelist;elseif (is_string($p_filelist))$v_list = explode($this->_separator, $p_filelist);else {$this->_cleanFile();$this->_error('Invalid file list');return false;}$v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);}if ($v_result) {$this->_writeFooter();$this->_close();} else$this->_cleanFile();return $v_result;}// }}}// {{{ addModify()/*** This method add the files / directories listed in $p_filelist at the* end of the existing archive. If the archive does not yet exists it* is created.* The $p_filelist parameter can be an array of string, each string* representing a filename or a directory name with their path if* needed. It can also be a single string with names separated by a* single blank.* The path indicated in $p_remove_dir will be removed from the* memorized path of each file / directory listed when this path* exists. By default nothing is removed (empty path '')* The path indicated in $p_add_dir will be added at the beginning of* the memorized path of each file / directory listed. However it can* be set to empty ''. The adding of a path is done after the removing* of path.* The path add/remove ability enables the user to prepare an archive* for extraction in a different path than the origin files are.* If a file/dir is already in the archive it will only be added at the* end of the archive. There is no update of the existing archived* file/dir. However while extracting the archive, the last file will* replace the first one. This results in a none optimization of the* archive size.* If a file/dir does not exist the file/dir is ignored. However an* error text is send to PEAR error.* If a file/dir is not readable the file/dir is ignored. However an* error text is send to PEAR error.** @param array $p_filelist An array of filenames and directory* names, or a single string with names* separated by a single blank space.* @param string $p_add_dir A string which contains a path to be* added to the memorized path of each* element in the list.* @param string $p_remove_dir A string which contains a path to be* removed from the memorized path of* each element in the list, when* relevant.* @return true on success, false on error.* @access public*/function addModify($p_filelist, $p_add_dir, $p_remove_dir=''){$v_result = true;if (!$this->_isArchive())$v_result = $this->createModify($p_filelist, $p_add_dir,$p_remove_dir);else {if (is_array($p_filelist))$v_list = $p_filelist;elseif (is_string($p_filelist))$v_list = explode($this->_separator, $p_filelist);else {$this->_error('Invalid file list');return false;}$v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);}return $v_result;}// }}}// {{{ addString()/*** This method add a single string as a file at the* end of the existing archive. If the archive does not yet exists it* is created.** @param string $p_filename A string which contains the full* filename path that will be associated* with the string.* @param string $p_string The content of the file added in* the archive.* @return true on success, false on error.* @access public*/function addString($p_filename, $p_string){$v_result = true;if (!$this->_isArchive()) {if (!$this->_openWrite()) {return false;}$this->_close();}if (!$this->_openAppend())return false;// Need to check the get back to the temporary file ? ....$v_result = $this->_addString($p_filename, $p_string);$this->_writeFooter();$this->_close();return $v_result;}// }}}// {{{ extractModify()/*** 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* @see extractList()*/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;}// }}}// {{{ extractInString()/*** This method extract from the archive one file identified by $p_filename.* The return value is a string with the file content, or NULL on error.* @param string $p_filename The path of the file to extract in a string.* @return a string with the file content or NULL.* @access public*/function extractInString($p_filename){if ($this->_openRead()) {$v_result = $this->_extractInString($p_filename);$this->_close();} else {$v_result = NULL;}return $v_result;}// }}}// {{{ extractList()/*** This method extract from the archive only the files indicated in the* $p_filelist. These files are extracted in the current directory or* in the directory indicated by the optional $p_path parameter.* If indicated the $p_remove_path can be used in the same way as it is* used in extractModify() method.* @param array $p_filelist An array of filenames and directory names,* or a single string with names separated* by a single blank space.* @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 true on success, false on error.* @access public* @see extractModify()*/function extractList($p_filelist, $p_path='', $p_remove_path=''){$v_result = true;$v_list_detail = array();if (is_array($p_filelist))$v_list = $p_filelist;elseif (is_string($p_filelist))$v_list = explode($this->_separator, $p_filelist);else {$this->_error('Invalid string list');return false;}if ($v_result = $this->_openRead()) {$v_result = $this->_extractList($p_path, $v_list_detail, "partial",$v_list, $p_remove_path);$this->_close();}return $v_result;}// }}}// {{{ setAttribute()/*** This method set specific attributes of the archive. It uses a variable* list of parameters, in the format attribute code + attribute values :* $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');* @param mixed $argv variable list of attributes and values* @return true on success, false on error.* @access public*/function setAttribute(){$v_result = true;// ----- Get the number of variable list of argumentsif (($v_size = func_num_args()) == 0) {return true;}// ----- Get the arguments$v_att_list = &func_get_args();// ----- Read the attributes$i=0;while ($i<$v_size) {// ----- Look for next optionswitch ($v_att_list[$i]) {// ----- Look for options that request a string valuecase ARCHIVE_TAR_ATT_SEPARATOR :// ----- Check the number of parametersif (($i+1) >= $v_size) {$this->_error('Invalid number of parameters for '.'attribute ARCHIVE_TAR_ATT_SEPARATOR');return false;}// ----- Get the value$this->_separator = $v_att_list[$i+1];$i++;break;default :$this->_error('Unknow attribute code '.$v_att_list[$i].'');return false;}// ----- Next attribute$i++;}return $v_result;}// }}}// {{{ _error()function _error($p_message){// ----- To be completed$this->raiseError($p_message);}// }}}// {{{ _warning()function _warning($p_message){// ----- To be completed$this->raiseError($p_message);}// }}}// {{{ _isArchive()function _isArchive($p_filename=NULL){if ($p_filename == NULL) {$p_filename = $this->_tarname;}clearstatcache();return @is_file($p_filename) && !@is_link($p_filename);}// }}}// {{{ _openWrite()function _openWrite(){if ($this->_compress_type == 'gz')$this->_file = @gzopen($this->_tarname, "wb9");else if ($this->_compress_type == 'bz2')$this->_file = @bzopen($this->_tarname, "w");else if ($this->_compress_type == 'none')$this->_file = @fopen($this->_tarname, "wb");else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');if ($this->_file == 0) {$this->_error('Unable to open in write mode \''.$this->_tarname.'\'');return false;}return true;}// }}}// {{{ _openRead()function _openRead(){if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {// ----- Look if a local copy need to be doneif ($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;if ($this->_compress_type == 'gz')$this->_file = @gzopen($v_filename, "rb");else if ($this->_compress_type == 'bz2')$this->_file = @bzopen($v_filename, "r");else if ($this->_compress_type == 'none')$this->_file = @fopen($v_filename, "rb");else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');if ($this->_file == 0) {$this->_error('Unable to open in read mode \''.$v_filename.'\'');return false;}return true;}// }}}// {{{ _openReadWrite()function _openReadWrite(){if ($this->_compress_type == 'gz')$this->_file = @gzopen($this->_tarname, "r+b");else if ($this->_compress_type == 'bz2') {$this->_error('Unable to open bz2 in read/write mode \''.$this->_tarname.'\' (limitation of bz2 extension)');return false;} else if ($this->_compress_type == 'none')$this->_file = @fopen($this->_tarname, "r+b");else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');if ($this->_file == 0) {$this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');return false;}return true;}// }}}// {{{ _close()function _close(){//if (isset($this->_file)) {if (is_resource($this->_file)) {if ($this->_compress_type == 'gz')@gzclose($this->_file);else if ($this->_compress_type == 'bz2')@bzclose($this->_file);else if ($this->_compress_type == 'none')@fclose($this->_file);else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');$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 : ToDoif ($this->_temp_tarname != '') {@unlink($this->_temp_tarname);$this->_temp_tarname = '';}return true;}// }}}// {{{ _cleanFile()function _cleanFile(){$this->_close();// ----- Look for a local copyif ($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;}// }}}// {{{ _writeBlock()function _writeBlock($p_binary_data, $p_len=null){if (is_resource($this->_file)) {if ($p_len === null) {if ($this->_compress_type == 'gz')@gzputs($this->_file, $p_binary_data);else if ($this->_compress_type == 'bz2')@bzwrite($this->_file, $p_binary_data);else if ($this->_compress_type == 'none')@fputs($this->_file, $p_binary_data);else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');} else {if ($this->_compress_type == 'gz')@gzputs($this->_file, $p_binary_data, $p_len);else if ($this->_compress_type == 'bz2')@bzwrite($this->_file, $p_binary_data, $p_len);else if ($this->_compress_type == 'none')@fputs($this->_file, $p_binary_data, $p_len);else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');}}return true;}// }}}// {{{ _readBlock()function _readBlock(){$v_block = null;if (is_resource($this->_file)) {if ($this->_compress_type == 'gz')$v_block = @gzread($this->_file, 512);else if ($this->_compress_type == 'bz2')$v_block = @bzread($this->_file, 512);else if ($this->_compress_type == 'none')$v_block = @fread($this->_file, 512);else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');}return $v_block;}// }}}// {{{ _jumpBlock()function _jumpBlock($p_len=null){if (is_resource($this->_file)) {if ($p_len === null)$p_len = 1;if ($this->_compress_type == 'gz') {@gzseek($this->_file, gztell($this->_file)+($p_len*512));}else if ($this->_compress_type == 'bz2') {// ----- Replace missing bztell() and bzseek()for ($i=0; $i<$p_len; $i++)$this->_readBlock();} else if ($this->_compress_type == 'none')@fseek($this->_file, ftell($this->_file)+($p_len*512));else$this->_error('Unknown or missing compression type ('.$this->_compress_type.')');}return true;}// }}}// {{{ _writeFooter()function _writeFooter(){if (is_resource($this->_file)) {// ----- Write the last 0 filled block for end of archive$v_binary_data = pack('a1024', '');$this->_writeBlock($v_binary_data);}return true;}// }}}// {{{ _addList()function _addList($p_list, $p_add_dir, $p_remove_dir){$v_result=true;$v_header = array();// ----- Remove potential windows directory separator$p_add_dir = $this->_translateWinPath($p_add_dir);$p_remove_dir = $this->_translateWinPath($p_remove_dir, false);if (!$this->_file) {$this->_error('Invalid file descriptor');return false;}if (sizeof($p_list) == 0)return true;foreach ($p_list as $v_filename) {if (!$v_result) {break;}// ----- Skip the current tar nameif ($v_filename == $this->_tarname)continue;if ($v_filename == '')continue;if (!file_exists($v_filename)) {$this->_warning("File '$v_filename' does not exist");continue;}// ----- Add the file or directory headerif (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))return false;if (@is_dir($v_filename) && !@is_link($v_filename)) {if (!($p_hdir = opendir($v_filename))) {$this->_warning("Directory '$v_filename' can not be read");continue;}while (false !== ($p_hitem = readdir($p_hdir))) {if (($p_hitem != '.') && ($p_hitem != '..')) {if ($v_filename != ".")$p_temp_list[0] = $v_filename.'/'.$p_hitem;else$p_temp_list[0] = $p_hitem;$v_result = $this->_addList($p_temp_list,$p_add_dir,$p_remove_dir);}}unset($p_temp_list);unset($p_hdir);unset($p_hitem);}}return $v_result;}// }}}// {{{ _addFile()function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir){if (!$this->_file) {$this->_error('Invalid file descriptor');return false;}if ($p_filename == '') {$this->_error('Invalid file name');return false;}// ----- Calculate the stored filename$p_filename = $this->_translateWinPath($p_filename, false);;$v_stored_filename = $p_filename;if (strcmp($p_filename, $p_remove_dir) == 0) {return true;}if ($p_remove_dir != '') {if (substr($p_remove_dir, -1) != '/')$p_remove_dir .= '/';if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)$v_stored_filename = substr($p_filename, strlen($p_remove_dir));}$v_stored_filename = $this->_translateWinPath($v_stored_filename);if ($p_add_dir != '') {if (substr($p_add_dir, -1) == '/')$v_stored_filename = $p_add_dir.$v_stored_filename;else$v_stored_filename = $p_add_dir.'/'.$v_stored_filename;}$v_stored_filename = $this->_pathReduction($v_stored_filename);if ($this->_isArchive($p_filename)) {if (($v_file = @fopen($p_filename, "rb")) == 0) {$this->_warning("Unable to open file '".$p_filename."' in binary read mode");return true;}if (!$this->_writeHeader($p_filename, $v_stored_filename))return false;while (($v_buffer = fread($v_file, 512)) != '') {$v_binary_data = pack("a512", "$v_buffer");$this->_writeBlock($v_binary_data);}fclose($v_file);} else {// ----- Only header for dirif (!$this->_writeHeader($p_filename, $v_stored_filename))return false;}return true;}// }}}// {{{ _addString()function _addString($p_filename, $p_string){if (!$this->_file) {$this->_error('Invalid file descriptor');return false;}if ($p_filename == '') {$this->_error('Invalid file name');return false;}// ----- Calculate the stored filename$p_filename = $this->_translateWinPath($p_filename, false);;if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),time(), 384, "", 0, 0))return false;$i=0;while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {$v_binary_data = pack("a512", $v_buffer);$this->_writeBlock($v_binary_data);}return true;}// }}}// {{{ _writeHeader()function _writeHeader($p_filename, $p_stored_filename){if ($p_stored_filename == '')$p_stored_filename = $p_filename;$v_reduce_filename = $this->_pathReduction($p_stored_filename);if (strlen($v_reduce_filename) > 99) {if (!$this->_writeLongHeader($v_reduce_filename))return false;}$v_info = lstat($p_filename);$v_uid = sprintf("%6s ", DecOct($v_info[4]));$v_gid = sprintf("%6s ", DecOct($v_info[5]));$v_perms = sprintf("%6s ", DecOct($v_info['mode']));$v_mtime = sprintf("%11s", DecOct($v_info['mode']));$v_linkname = '';if (@is_link($p_filename)) {$v_typeflag = '2';$v_linkname = readlink($p_filename);$v_size = sprintf("%11s ", DecOct(0));} elseif (@is_dir($p_filename)) {$v_typeflag = "5";$v_size = sprintf("%11s ", DecOct(0));} else {$v_typeflag = '';clearstatcache();$v_size = sprintf("%11s ", DecOct($v_info['size']));}$v_magic = '';$v_version = '';$v_uname = '';$v_gname = '';$v_devmajor = '';$v_devminor = '';$v_prefix = '';$v_binary_data_first = pack("a100a8a8a8a12A12",$v_reduce_filename, $v_perms, $v_uid,$v_gid, $v_size, $v_mtime);$v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",$v_typeflag, $v_linkname, $v_magic,$v_version, $v_uname, $v_gname,$v_devmajor, $v_devminor, $v_prefix, '');// ----- Calculate the checksum$v_checksum = 0;// ..... First part of the headerfor ($i=0; $i<148; $i++)$v_checksum += ord(substr($v_binary_data_first,$i,1));// ..... Ignore the checksum value and replace it by ' ' (space)for ($i=148; $i<156; $i++)$v_checksum += ord(' ');// ..... Last part of the headerfor ($i=156, $j=0; $i<512; $i++, $j++)$v_checksum += ord(substr($v_binary_data_last,$j,1));// ----- Write the first 148 bytes of the header in the archive$this->_writeBlock($v_binary_data_first, 148);// ----- Write the calculated checksum$v_checksum = sprintf("%6s ", DecOct($v_checksum));$v_binary_data = pack("a8", $v_checksum);$this->_writeBlock($v_binary_data, 8);// ----- Write the last 356 bytes of the header in the archive$this->_writeBlock($v_binary_data_last, 356);return true;}// }}}// {{{ _writeHeaderBlock()function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,$p_type='', $p_uid=0, $p_gid=0){$p_filename = $this->_pathReduction($p_filename);if (strlen($p_filename) > 99) {if (!$this->_writeLongHeader($p_filename))return false;}if ($p_type == "5") {$v_size = sprintf("%11s ", DecOct(0));} else {$v_size = sprintf("%11s ", DecOct($p_size));}$v_uid = sprintf("%6s ", DecOct($p_uid));$v_gid = sprintf("%6s ", DecOct($p_gid));$v_perms = sprintf("%6s ", DecOct($p_perms));$v_mtime = sprintf("%11s", DecOct($p_mtime));$v_linkname = '';$v_magic = '';$v_version = '';$v_uname = '';$v_gname = '';$v_devmajor = '';$v_devminor = '';$v_prefix = '';$v_binary_data_first = pack("a100a8a8a8a12A12",$p_filename, $v_perms, $v_uid, $v_gid,$v_size, $v_mtime);$v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",$p_type, $v_linkname, $v_magic,$v_version, $v_uname, $v_gname,$v_devmajor, $v_devminor, $v_prefix, '');// ----- Calculate the checksum$v_checksum = 0;// ..... First part of the headerfor ($i=0; $i<148; $i++)$v_checksum += ord(substr($v_binary_data_first,$i,1));// ..... Ignore the checksum value and replace it by ' ' (space)for ($i=148; $i<156; $i++)$v_checksum += ord(' ');// ..... Last part of the headerfor ($i=156, $j=0; $i<512; $i++, $j++)$v_checksum += ord(substr($v_binary_data_last,$j,1));// ----- Write the first 148 bytes of the header in the archive$this->_writeBlock($v_binary_data_first, 148);// ----- Write the calculated checksum$v_checksum = sprintf("%6s ", DecOct($v_checksum));$v_binary_data = pack("a8", $v_checksum);$this->_writeBlock($v_binary_data, 8);// ----- Write the last 356 bytes of the header in the archive$this->_writeBlock($v_binary_data_last, 356);return true;}// }}}// {{{ _writeLongHeader()function _writeLongHeader($p_filename){$v_size = sprintf("%11s ", DecOct(strlen($p_filename)));$v_typeflag = 'L';$v_linkname = '';$v_magic = '';$v_version = '';$v_uname = '';$v_gname = '';$v_devmajor = '';$v_devminor = '';$v_prefix = '';$v_binary_data_first = pack("a100a8a8a8a12A12",'././@LongLink', 0, 0, 0, $v_size, 0);$v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",$v_typeflag, $v_linkname, $v_magic,$v_version, $v_uname, $v_gname,$v_devmajor, $v_devminor, $v_prefix, '');// ----- Calculate the checksum$v_checksum = 0;// ..... First part of the headerfor ($i=0; $i<148; $i++)$v_checksum += ord(substr($v_binary_data_first,$i,1));// ..... Ignore the checksum value and replace it by ' ' (space)for ($i=148; $i<156; $i++)$v_checksum += ord(' ');// ..... Last part of the headerfor ($i=156, $j=0; $i<512; $i++, $j++)$v_checksum += ord(substr($v_binary_data_last,$j,1));// ----- Write the first 148 bytes of the header in the archive$this->_writeBlock($v_binary_data_first, 148);// ----- Write the calculated checksum$v_checksum = sprintf("%6s ", DecOct($v_checksum));$v_binary_data = pack("a8", $v_checksum);$this->_writeBlock($v_binary_data, 8);// ----- Write the last 356 bytes of the header in the archive$this->_writeBlock($v_binary_data_last, 356);// ----- Write the filename as content of the block$i=0;while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {$v_binary_data = pack("a512", "$v_buffer");$this->_writeBlock($v_binary_data);}return true;}// }}}// {{{ _readHeader()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;}if (!is_array($v_header)) {$v_header = array();}// ----- Calculate the checksum$v_checksum = 0;// ..... First part of the headerfor ($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 headerfor ($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']);if ($this->_maliciousFilename($v_header['filename'])) {$this->_error('Malicious .tar detected, file "' . $v_header['filename'] .'" will not install in desired directory tree');return false;}$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;}$v_header['link'] = trim($v_data['link']);/* ----- All these fields are removed form the header becausethey do not carry interesting info$v_header[magic] = trim($v_data[magic]);$v_header[version] = trim($v_data[version]);$v_header[uname] = trim($v_data[uname]);$v_header[gname] = trim($v_data[gname]);$v_header[devmajor] = trim($v_data[devmajor]);$v_header[devminor] = trim($v_data[devminor]);*/return true;}// }}}// {{{ _maliciousFilename()/*** Detect and report a malicious file name** @param string $file* @return bool* @access private*/function _maliciousFilename($file){if (strpos($file, '/../') !== false) {return true;}if (strpos($file, '../') === 0) {return true;}return false;}// }}}// {{{ _readLongHeader()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_filename = trim($v_filename);$v_header['filename'] = $v_filename;if ($this->_maliciousFilename($v_filename)) {$this->_error('Malicious .tar detected, file "' . $v_filename .'" will not install in desired directory tree');return false;}return true;}// }}}// {{{ _extractInString()/*** This method extract from the archive one file identified by $p_filename.* The return value is a string with the file content, or NULL on error.* @param string $p_filename The path of the file to extract in a string.* @return a string with the file content or NULL.* @access private*/function _extractInString($p_filename){$v_result_str = "";While (strlen($v_binary_data = $this->_readBlock()) != 0){if (!$this->_readHeader($v_binary_data, $v_header))return NULL;if ($v_header['filename'] == '')continue;// ----- Look for long filenameif ($v_header['typeflag'] == 'L') {if (!$this->_readLongHeader($v_header))return NULL;}if ($v_header['filename'] == $p_filename) {if ($v_header['typeflag'] == "5") {$this->_error('Unable to extract in string a directory '.'entry {'.$v_header['filename'].'}');return NULL;} else {$n = floor($v_header['size']/512);for ($i=0; $i<$n; $i++) {$v_result_str .= $this->_readBlock();}if (($v_header['size'] % 512) != 0) {$v_content = $this->_readBlock();$v_result_str .= substr($v_content, 0,($v_header['size'] % 512));}return $v_result_str;}} else {$this->_jumpBlock(ceil(($v_header['size']/512)));}}return NULL;}// }}}// {{{ _extractList()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 filenameif ($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 directoryif (substr($p_file_list[$i], -1) == '/') {// ----- Look if the directory is in the filename pathif ((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 nameselseif ($p_file_list[$i] == $v_header['filename']) {$v_extract_file = TRUE;break;}}} else {$v_extract_file = TRUE;}// ----- Look if this file need to be extractedif (($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 necessaryelseif (($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;}}} elseif ($v_header['typeflag'] == "2") {if (@file_exists($v_header['filename'])) {@unlink($v_header['filename']);}if (!@symlink($v_header['link'], $v_header['filename'])) {$this->_error('Unable to extract symbolic link {'.$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']);if ($v_header['mode'] & 0111) {// make file executable, obey umask$mode = fileperms($v_header['filename']) | (~umask() & 0111);@chmod($v_header['filename'], $mode);}}// ----- Check the file sizeclearstatcache();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 filesif (($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;if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {return true;}}}return true;}// }}}// {{{ _openAppend()function _openAppend(){if (filesize($this->_tarname) == 0)return $this->_openWrite();if ($this->_compress) {$this->_close();if (!@rename($this->_tarname, $this->_tarname.".tmp")) {$this->_error('Error while renaming \''.$this->_tarname.'\' to temporary file \''.$this->_tarname.'.tmp\'');return false;}if ($this->_compress_type == 'gz')$v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");elseif ($this->_compress_type == 'bz2')$v_temp_tar = @bzopen($this->_tarname.".tmp", "r");if ($v_temp_tar == 0) {$this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');@rename($this->_tarname.".tmp", $this->_tarname);return false;}if (!$this->_openWrite()) {@rename($this->_tarname.".tmp", $this->_tarname);return false;}if ($this->_compress_type == 'gz') {while (!@gzeof($v_temp_tar)) {$v_buffer = @gzread($v_temp_tar, 512);if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {// do not copy end blocks, we will re-make them// after appendingcontinue;}$v_binary_data = pack("a512", $v_buffer);$this->_writeBlock($v_binary_data);}@gzclose($v_temp_tar);}elseif ($this->_compress_type == 'bz2') {while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {continue;}$v_binary_data = pack("a512", $v_buffer);$this->_writeBlock($v_binary_data);}@bzclose($v_temp_tar);}if (!@unlink($this->_tarname.".tmp")) {$this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');}} else {// ----- For not compressed tar, just add files before the last// one or two 512 bytes blockif (!$this->_openReadWrite())return false;clearstatcache();$v_size = filesize($this->_tarname);// We might have zero, one or two end blocks.// The standard is two, but we should try to handle// other cases.fseek($this->_file, $v_size - 1024);if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {fseek($this->_file, $v_size - 1024);}elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {fseek($this->_file, $v_size - 512);}}return true;}// }}}// {{{ _append()function _append($p_filelist, $p_add_dir='', $p_remove_dir=''){if (!$this->_openAppend())return false;if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))$this->_writeFooter();$this->_close();return true;}// }}}// {{{ _dirCheck()/*** 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*/function _dirCheck($p_dir){clearstatcache();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;}// }}}// {{{ _pathReduction()/*** Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",* rand emove double slashes.** @param string $p_dir path to reduce** @return string reduced path** @access private**/function _pathReduction($p_dir){$v_result = '';// ----- Look for not empty pathif ($p_dir != '') {// ----- Explode path by directory names$v_list = explode('/', $p_dir);// ----- Study directories from last to firstfor ($i=sizeof($v_list)-1; $i>=0; $i--) {// ----- Look for current pathif ($v_list[$i] == ".") {// ----- Ignore this directory// Should be the first $i=0, but no check is done}else if ($v_list[$i] == "..") {// ----- Ignore it and ignore the $i-1$i--;}else if ( ($v_list[$i] == '')&& ($i!=(sizeof($v_list)-1))&& ($i!=0)) {// ----- Ignore only the double '//' in path,// but not the first and last /} else {$v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');}}}$v_result = strtr($v_result, '\\', '/');return $v_result;}// }}}// {{{ _translateWinPath()function _translateWinPath($p_path, $p_remove_disk_letter=true){if (defined('OS_WINDOWS') && OS_WINDOWS) {// ----- Look for potential disk letterif ( ($p_remove_disk_letter)&& (($v_position = strpos($p_path, ':')) != false)) {$p_path = substr($p_path, $v_position+1);}// ----- Change potential windows directory separatorif ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {$p_path = strtr($p_path, '\\', '/');}}return $p_path;}// }}}}?>