* Invision Power Services * IP.Board v3.4.9 * 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) $ * * * @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: * * 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(); * * * * Exception Codes: * * */ 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, '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; } } }