740 lines
15 KiB
PHP
740 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* <pre>
|
|
* Invision Power Services
|
|
* IP.Board v3.4.5
|
|
* XML Archive Hanlder: Can create XML Archives (gzips) of multiple files, binary and ascii
|
|
* Last Updated: $Date: 2012-05-10 16:10:13 -0400 (Thu, 10 May 2012) $
|
|
* </pre>
|
|
*
|
|
* @author $Author: bfarber $
|
|
* @copyright (c) 2001 - 2009 Invision Power Services, Inc.
|
|
* @license http://www.invisionpower.com/company/standards.php#license
|
|
* @package IP.Board
|
|
* @subpackage Kernel
|
|
* @link http://www.invisionpower.com
|
|
* @since 25th February 2004
|
|
* @version $Revision: 10721 $
|
|
*
|
|
*
|
|
* Example Usage:
|
|
* <code>
|
|
* Sample file set:
|
|
*
|
|
* someDir/
|
|
* someDir/index.html
|
|
* someDir/file.html
|
|
* someDir/images/
|
|
* someDir/images/pic.jpg
|
|
* anotherDir/
|
|
* anotherDir/anotherFile.html
|
|
*
|
|
* # READING ARCHIVE
|
|
* $archive = new classXMLArchive();
|
|
* try
|
|
* {
|
|
* $archive->read( "/path/to/archive.xml" );
|
|
* }
|
|
* catch ( Exception $err ) {}
|
|
*
|
|
* print $archive->getFile('someDir/file.html');
|
|
* print_r( $archive->asArray() );
|
|
*
|
|
* # CREATING ARCHIVE
|
|
*
|
|
* $archive = new classXMLArchive();
|
|
*
|
|
* try
|
|
* {
|
|
* $archive->add( "someDir" );
|
|
* $archive->add( "anotherDir/anotherFile.html" );
|
|
* $archive->add( "Create a new file from scratch!", "anotherDir/brandNewFile.html" );
|
|
* }
|
|
* catch ( Exception $err ) {}
|
|
*
|
|
* # Save gzipped
|
|
* $archive->saveGZIP( "/path/to/archive.xml.gzip" );
|
|
* # Save normally
|
|
* $archive->save( "/path/to/archive.xml" );
|
|
* # Just return the data
|
|
* $archive->getArchiveContents();
|
|
* </code>
|
|
*
|
|
*
|
|
* Exception Codes:
|
|
* <ul>
|
|
* <li>FILE_OR_DIR_NOT_EXISTS: No such directory or file
|
|
* <li>NOT_A_DIR: Not a directory
|
|
* <li>CANNOT_WRITE_TO_DISK: Could not write archive to disk
|
|
* <li>NOTHING_TO_SAVE: No XML document to save
|
|
* <li>COULD_NOT_LOAD_XML_DATA: Could not load xml data
|
|
* <li>WRITE_NOT_VALID_INPUT: The input (XML or filename) is not valid
|
|
* <li>NO_FILES_TO_WRITE: The archive contains nothing to write back
|
|
* </ul>
|
|
*
|
|
*/
|
|
|
|
class classXMLArchive
|
|
{
|
|
/**
|
|
* XML Object
|
|
*
|
|
* @var object
|
|
*/
|
|
protected $_xml = null;
|
|
|
|
/**
|
|
* File array
|
|
*
|
|
* @var array
|
|
*/
|
|
public $_fileArray = array();
|
|
|
|
/**
|
|
* Error number
|
|
*
|
|
* @var integer
|
|
*/
|
|
public $error_number = 0;
|
|
|
|
/**
|
|
* Error message
|
|
*
|
|
* @var string
|
|
*/
|
|
public $error_message = "";
|
|
|
|
/**
|
|
* Work files
|
|
*
|
|
* @var array
|
|
*/
|
|
public $_workFiles = array();
|
|
|
|
/**
|
|
* Non binary file extensions
|
|
*
|
|
* @var string
|
|
*/
|
|
public $non_binary = 'txt htm html xml css js cgi php php3';
|
|
|
|
/**
|
|
* Strip path
|
|
*
|
|
* @var string
|
|
*/
|
|
public $_stripPathString = "";
|
|
|
|
/**
|
|
* Allow hidden files
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_allowHiddenFiles = FALSE;
|
|
|
|
/**
|
|
* Root tag values
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_defaultRootValues = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string Script root path
|
|
* @return @e void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
//-----------------------------------
|
|
// Get the XML class
|
|
//-----------------------------------
|
|
|
|
require_once( dirname( __FILE__ ) . '/classXML.php' );/*noLibHook*/
|
|
$this->_xml = new classXML( IPS_DOC_CHAR_SET );
|
|
|
|
/* Set up the root values */
|
|
$this->_defaultRootValues = array( 'generator' => 'IPS_KERNEL', 'created' => time() );
|
|
|
|
$this->error_number = 0;
|
|
}
|
|
|
|
/**
|
|
* Adds values to the root tag
|
|
*
|
|
* @return @e void
|
|
*/
|
|
public function addRootTagValues( $array=array() )
|
|
{
|
|
if ( is_array( $array ) AND count( $array ) )
|
|
{
|
|
foreach( $array as $k => $v )
|
|
{
|
|
if ( $k and $v != '' )
|
|
{
|
|
$this->_defaultRootValues[ $k ] = $v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allow hidden files to be added when using addDir
|
|
*
|
|
* @return @e void
|
|
*/
|
|
public function allowHiddenFiles()
|
|
{
|
|
$this->_allowHiddenFiles = TRUE;
|
|
}
|
|
|
|
/**
|
|
* Set the path to 'strip' from the path data
|
|
*
|
|
* @param string Full path to remove
|
|
* @return @e void
|
|
*/
|
|
public function setStripPath( $path )
|
|
{
|
|
/* Ensure that there is no trailing slash */
|
|
$path = rtrim( $path, '/' );
|
|
|
|
$this->_stripPathString = $path;
|
|
}
|
|
|
|
/**
|
|
* Return file data as array
|
|
*
|
|
* @return @e array
|
|
*/
|
|
public function asArray()
|
|
{
|
|
return $this->_fileArray;
|
|
}
|
|
|
|
/**
|
|
* Return a file from the fileArray
|
|
*
|
|
* @param string Key
|
|
* @return @e string
|
|
*/
|
|
public function getFile( $key )
|
|
{
|
|
if ( isset( $this->_fileArray[ $key ] ) )
|
|
{
|
|
return $this->_fileArray[ $key ]['content'];
|
|
}
|
|
else
|
|
{
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return number of files contained in the expanded archive
|
|
*
|
|
* @return @e integer
|
|
*/
|
|
public function countFileArray()
|
|
{
|
|
return intval( count( $this->_fileArray ) );
|
|
}
|
|
|
|
/**
|
|
* Read an XML document from disk
|
|
*
|
|
* @param string File name
|
|
* @return @e void
|
|
*/
|
|
public function read( $filename )
|
|
{
|
|
if ( is_file( $filename ) )
|
|
{
|
|
if ( strstr( $filename, '.gz' ) )
|
|
{
|
|
if ( $FH = @gzopen( $filename, 'r' ) )
|
|
{
|
|
$data = @gzread( $FH, $filename );
|
|
@gzclose( $FH );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "COULD_NOT_LOAD_XML_DATA" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( $FH = @fopen( $filename, 'r' ) )
|
|
{
|
|
$data = @fread( $FH, filesize( $filename ) );
|
|
@fclose( $FH );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "COULD_NOT_LOAD_XML_DATA" );
|
|
}
|
|
}
|
|
|
|
$this->readXML( $data );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "FILE_OR_DIR_NOT_EXISTS" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read an XML document from passed data
|
|
*
|
|
* @param string Raw XML Data
|
|
* @return @e void
|
|
*/
|
|
public function readXML( $data )
|
|
{
|
|
if ( $data )
|
|
{
|
|
$this->_xml->loadXML( $data );
|
|
|
|
foreach( $this->_xml->fetchElements('file') as $file )
|
|
{
|
|
$_file = $this->_xml->fetchElementsFromRecord( $file );
|
|
$_file['content'] = base64_decode( preg_replace( "/\s/", "", $_file['content'] ) );
|
|
|
|
$this->_fileArray[ ltrim( $_file['path'] . '/' . $_file['filename'], '/' ) ] = $_file;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write out the XML document as a GZIP file
|
|
*
|
|
* @param string Filename
|
|
* @return @e void
|
|
*/
|
|
public function saveGZIP( $filename )
|
|
{
|
|
$this->_create();
|
|
|
|
if ( $this->_xml->fetchDocument() )
|
|
{
|
|
if ( $FH = @gzopen( $filename, 'wb' ) )
|
|
{
|
|
@gzwrite( $FH, $this->_xml->fetchDocument() );
|
|
@gzclose( $FH );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "CANNOT_WRITE_TO_DISK" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "NOTHING_TO_SAVE" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write out the XML document as a normal file
|
|
*
|
|
* @param string FIlename
|
|
* @return @e void
|
|
*/
|
|
public function save( $filename )
|
|
{
|
|
$this->_create();
|
|
|
|
$_doc = $this->_xml->fetchDocument();
|
|
|
|
if ( $_doc )
|
|
{
|
|
if ( $FH = @fopen( $filename, 'wb' ) )
|
|
{
|
|
@fwrite( $FH, $_doc );
|
|
@fclose( $FH );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "CANNOT_WRITE_TO_DISK" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "NOTHING_TO_SAVE" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method of getting XML document from this class
|
|
*
|
|
* @return string XML document
|
|
*/
|
|
public function getArchiveContents()
|
|
{
|
|
$this->_create();
|
|
return $this->_xml->fetchDocument();
|
|
}
|
|
|
|
/**
|
|
* Write the archive back to disk
|
|
*
|
|
* Note: Ensure that if you intend to make XMLArchives available for others
|
|
* that you create it with setStripPath otherwise you may get unexpected results!
|
|
*
|
|
* @param mixed Filename (must end with .xml or .gz) or raw data
|
|
* @param string Base path to write to
|
|
* @return @e boolean
|
|
* @throws Exception
|
|
*
|
|
* Exception Codes:
|
|
* WRITE_NOT_VALID_INPUT The input (XML or filename) is not valid
|
|
* NO_FILES_TO_WRITE The archive contains nothing to write back
|
|
*/
|
|
public function write( $input, $basePath='/' )
|
|
{
|
|
/* Gather the data */
|
|
if ( substr( $input, -3 ) == '.gz' OR substr( $input, -3 ) == '.xml' )
|
|
{
|
|
try
|
|
{
|
|
$this->read( $input );
|
|
}
|
|
catch( Exception $e )
|
|
{
|
|
return $e->getMessage();
|
|
}
|
|
}
|
|
else if ( strstr( $input, '<xmlarchive' ) )
|
|
{
|
|
$this->readXML( $input );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "WRITE_NOT_VALID_INPUT" );
|
|
}
|
|
|
|
/* Did we get anything? */
|
|
if ( ! $this->countFileArray() )
|
|
{
|
|
throw new Exception( "NO_FILES_TO_WRITE" );
|
|
}
|
|
|
|
$basePath = rtrim( $basePath, '/' );
|
|
|
|
/* So, write 'em! */
|
|
$files = $this->asArray();
|
|
|
|
foreach( $files as $path => $data )
|
|
{
|
|
$path = trim( $path, '/' );
|
|
|
|
if ( $this->_writeContents( $basePath . '/' . $path, $data['content'] ) !== TRUE )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Done */
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Main "Add" function handles a lot of stuff.
|
|
* It's good like that!
|
|
*
|
|
* @param string Filename or data
|
|
* @param string File to store as
|
|
* @return @e mixed
|
|
*/
|
|
public function add( $data, $saveName='' )
|
|
{
|
|
/* Is this just data? */
|
|
if ( $saveName )
|
|
{
|
|
return $this->addData( $data, $saveName );
|
|
}
|
|
else if ( is_dir( $data ) )
|
|
{
|
|
return $this->addDirectory( $data );
|
|
}
|
|
else
|
|
{
|
|
return $this->addFile( $data );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shorthand function
|
|
*
|
|
* @return @e mixed
|
|
* @see addDirectory()
|
|
*/
|
|
public function addDir( $dir )
|
|
{
|
|
return $this->addDirectory( $dir );
|
|
}
|
|
|
|
/**
|
|
* Add directory to archive
|
|
*
|
|
* @param string Directory to add
|
|
* @return @e mixed
|
|
* @throws Exception
|
|
*/
|
|
public function addDirectory( $dir )
|
|
{
|
|
//-----------------------------------
|
|
// Got dir?
|
|
//-----------------------------------
|
|
|
|
if ( ! is_dir($dir) )
|
|
{
|
|
throw new Exception( "FILE_OR_DIR_NOT_EXISTS" );
|
|
return false;
|
|
}
|
|
|
|
$dir = rtrim( $dir, '/' );
|
|
|
|
//-----------------------------------
|
|
// Populate this->workfiles
|
|
//-----------------------------------
|
|
|
|
$this->_workFiles = array();
|
|
$this->_getDirContents( $dir );
|
|
|
|
//-----------------------------------
|
|
// Add them into the file array
|
|
//-----------------------------------
|
|
|
|
foreach ( $this->_workFiles as $f )
|
|
{
|
|
$this->addFile( $f );
|
|
}
|
|
|
|
$this->_workFiles = array();
|
|
}
|
|
|
|
/**
|
|
* Add file to archive
|
|
*
|
|
* @param string File to add
|
|
* @param array Extra tags
|
|
* @return @e mixed
|
|
*/
|
|
public function addFile( $filename, $extra_tags=array() )
|
|
{
|
|
//-----------------------------------
|
|
// Kill hidden files
|
|
//-----------------------------------
|
|
|
|
$_temp = explode( '/', $filename );
|
|
|
|
$actual_file = array_pop( $_temp );
|
|
|
|
if ( in_array( strtolower($actual_file), array( '.ds_store', 'thumbs.db' ) ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( file_exists( $filename ) )
|
|
{
|
|
if ( $FH = @fopen( $filename, 'rb' ) )
|
|
{
|
|
$data = @fread( $FH, filesize( $filename ) );
|
|
@fclose( $FH );
|
|
}
|
|
|
|
$this->addData( $data, $filename, $extra_tags );
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "FILE_OR_DIR_NOT_EXISTS" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add filecontents to archive
|
|
*
|
|
* @param string File data
|
|
* @param string File name
|
|
* @param array Extra tags
|
|
* @return @e void
|
|
*/
|
|
public function addData( $data, $filename, $extra_tags=array() )
|
|
{
|
|
$ext = preg_replace( "/.*\.(.+?)$/", "\\1", $filename );
|
|
|
|
$binary = 1;
|
|
|
|
//-----------------------------------
|
|
// ASCII?
|
|
//-----------------------------------
|
|
|
|
if ( strstr( ' ' . $this->non_binary . ' ', ' '.$ext.' ' ) )
|
|
{
|
|
$binary = 0;
|
|
}
|
|
|
|
//-----------------------------------
|
|
// Get dir / filename
|
|
//-----------------------------------
|
|
|
|
$dir_path = array();
|
|
$dir_path = explode( "/", $filename );
|
|
|
|
if ( count( $dir_path ) )
|
|
{
|
|
$real_filename = array_pop( $dir_path );
|
|
}
|
|
|
|
$real_filename = $real_filename ? $real_filename : $filename;
|
|
|
|
$path = implode( "/", $dir_path );
|
|
|
|
$path = $this->_stripPath( $path );
|
|
|
|
$this_array = array(
|
|
'filename' => $real_filename,
|
|
'content' => $data,
|
|
'path' => $path,
|
|
'binary' => $binary
|
|
);
|
|
|
|
foreach( $extra_tags as $k => $v )
|
|
{
|
|
if ( $k and ! in_array( $k, array_keys($this_array) ) )
|
|
{
|
|
$this_array[ $k ] = $v;
|
|
}
|
|
}
|
|
|
|
$this->_fileArray[] = $this_array;
|
|
}
|
|
|
|
/**
|
|
* Create the XML archive
|
|
*
|
|
* @return @e void
|
|
*/
|
|
protected function _create()
|
|
{
|
|
$this->_xml->newXMLDocument();
|
|
$this->_xml->addElement( 'xmlarchive', '', (array)$this->_defaultRootValues );
|
|
$this->_xml->addElement( 'fileset', 'xmlarchive' );
|
|
|
|
foreach( $this->_fileArray as $f )
|
|
{
|
|
$f['content'] = chunk_split(base64_encode($f['content']));
|
|
|
|
$this->_xml->addElementAsRecord( 'fileset', 'file', $f );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write contents of a file to disk
|
|
* Creates directories, etc as it goes
|
|
*
|
|
* @param string Path with filename to write to
|
|
* @param string Data to write
|
|
* @return @e boolean
|
|
*/
|
|
protected function _writeContents( $path, $content )
|
|
{
|
|
$path = $this->_stripPath( $path );
|
|
$_path = DOC_IPS_ROOT_PATH;
|
|
$_dirParts = explode( '/', str_replace( DOC_IPS_ROOT_PATH, '', $path ) );
|
|
$file = array_pop( $_dirParts );
|
|
|
|
foreach( $_dirParts as $_p )
|
|
{
|
|
$_path .= '/' . $_p;
|
|
|
|
if ( ! is_dir( $_path ) )
|
|
{
|
|
if ( ! @mkdir( $_path, IPS_FOLDER_PERMISSION ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
@chmod( $_path, IPS_FOLDER_PERMISSION );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! @file_put_contents( $_path . '/' . $file, $content ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
@chmod( $_path . '/' . $file, IPS_FILE_PERMISSION );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Strip path information from the real path
|
|
*
|
|
* @param string Input path
|
|
* @return @e string
|
|
*/
|
|
protected function _stripPath( $path )
|
|
{
|
|
if ( $this->_stripPathString )
|
|
{
|
|
$path = trim( str_ireplace( $this->_stripPathString, '', $path ), '/' );
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Get directory contents
|
|
*
|
|
* @param string Directory
|
|
* @return @e boolean
|
|
*/
|
|
protected function _getDirContents( $dir )
|
|
{
|
|
if ( file_exists( $dir ) AND is_dir( $dir ) )
|
|
{
|
|
try
|
|
{
|
|
foreach( new DirectoryIterator( $dir ) as $file )
|
|
{
|
|
if ( ! $file->isDot() )
|
|
{
|
|
$filename = $file->getFileName();
|
|
|
|
/* Allow files? */
|
|
if ( $this->_allowHiddenFiles !== TRUE )
|
|
{
|
|
if ( substr( $filename, 0, 1 ) == '.' )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( is_dir( $dir."/".$filename ) )
|
|
{
|
|
$this->_getDirContents( $dir . "/" . $filename );
|
|
}
|
|
else
|
|
{
|
|
$this->_workFiles[] = $dir . "/" . $filename;
|
|
}
|
|
}
|
|
}
|
|
} catch ( Exception $e ) {}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception( "FILE_OR_DIR_NOT_EXISTS" );
|
|
return false;
|
|
}
|
|
}
|
|
}
|