summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/util/vm/VmwareConfig.java
blob: ac13e4f2bedae8c4965f6c5096d01c1a4d0cc979 (plain) (tree)
































                                                                                                  
                                                                                                  




















                                                                        
                                                                                                       













                                                                                
                                                                                                         



                                           

                                                                                                       
         
                     
                                                
                                                                                   


                                                                        
 
                                                      


                                                                                                                                      


                                                                                  


                                                                                                        




                                                                                               


                                                                                                     
                                                                                                                            

         

                                                                         








                                                                                                   
                          

         











                                                         
                                                            























                                                                                                                                       

                                                                                                                                                      
 
                                                        







                                                                  
                                                                                  





















                                                                           




                                        









                                                                                     
                                                                               


                                                                               
                                                                                                                           


























































                                                                 
         
 
package org.openslx.util.vm;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.openslx.util.Util;

public class VmwareConfig
{

	private static final Logger LOGGER = Logger.getLogger( VmwareConfig.class );

	private Map<String, ConfigEntry> entries = new TreeMap<>( String.CASE_INSENSITIVE_ORDER );

	public VmwareConfig()
	{
		// (void)
	}

	public VmwareConfig( File file ) throws IOException, UnsupportedVirtualizerFormatException
	{
		int todo = (int)Math.min( 100000, file.length() );
		int offset = 0;
		byte[] data = new byte[ todo ];
		FileInputStream fr = null;
		try {
			fr = new FileInputStream( file );
			while ( todo > 0 ) {
				int ret = fr.read( data, offset, todo );
				if ( ret <= 0 )
					break;
				todo -= ret;
				offset += ret;
			}
		} finally {
			Util.safeClose( fr );
		}
		init( data, offset );

	}

	public VmwareConfig( InputStream is ) throws IOException, UnsupportedVirtualizerFormatException
	{
		int todo = Math.max( 4000, Math.min( 100000, is.available() ) );
		int offset = 0;
		byte[] data = new byte[ todo ];
		while ( todo > 0 ) {
			int ret = is.read( data, offset, todo );
			if ( ret <= 0 )
				break;
			todo -= ret;
			offset += ret;
		}
		init( data, offset );
	}

	public VmwareConfig( byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException
	{
		init( vmxContent, length );
	}

	// function is used for both .vmx and .vmdk files
	private void init( byte[] vmxContent, int length ) throws UnsupportedVirtualizerFormatException
	{
		try {
			boolean isValid = false;
			BufferedReader reader = getVmxReader( vmxContent, length );
			String line;
			while ( ( line = reader.readLine() ) != null ) {
				KeyValuePair entry = parse( line );

				if ( entry != null ) {
					if ( entry.key.equals( "virtualHW.version" ) || entry.key.equals( "ddb.virtualHWVersion" ) ) {
						isValid = true;
					}
					set( entry.key, unescape( entry.value ) );
				}
			}
			if ( !isValid ) {
				throw new UnsupportedVirtualizerFormatException( "Not in VMX format." );
			}
		} catch ( IOException e ) {
			LOGGER.warn( "Exception when loading vmx from byte array (how!?)", e );
		}
	}

	public static BufferedReader getVmxReader( byte[] vmxContent, int length ) throws IOException
	{
		Charset cs = getCharset( vmxContent, length );
		return new BufferedReader( new InputStreamReader( new ByteArrayInputStream( vmxContent, 0, length ), cs ) );
	}

	public static Charset getCharset( byte[] vmxContent, int length )
	{
		String csName = detectCharset( new ByteArrayInputStream( vmxContent, 0, length ) );
		Charset cs = null;
		try {
			cs = Charset.forName( csName );
		} catch ( Exception e ) {
			LOGGER.warn( "Could not instantiate charset " + csName, e );
		}
		if ( cs == null )
			cs = StandardCharsets.ISO_8859_1;
		return cs;
	}

	private String unescape( String value )
	{
		String ret = value;
		if ( ret.contains( "|22" ) ) {
			ret = ret.replace( "|22", "\"" );
		}
		if ( ret.contains( "|7C" ) ) {
			ret.replace( "|7C", "|" );
		}
		return ret;
	}

	public static String detectCharset( InputStream is )
	{
		try {
			BufferedReader csDetectReader = new BufferedReader( new InputStreamReader( is, StandardCharsets.ISO_8859_1 ) );
			String line;
			while ( ( line = csDetectReader.readLine() ) != null ) {
				KeyValuePair entry = parse( line );
				if ( entry == null )
					continue;
				if ( entry.key.equals( ".encoding" ) || entry.key.equals( "encoding" ) ) {
					return entry.value;
				}
			}
		} catch ( Exception e ) {
			LOGGER.warn( "Could not detect charset, fallback to latin1", e );
		}
		// Dumb fallback
		return "ISO-8859-1";
	}

	public Set<Entry<String, ConfigEntry>> entrySet()
	{
		return entries.entrySet();
	}

	private static final Pattern settingMatcher1 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*\"(.*)\"\\s*$", Pattern.CASE_INSENSITIVE );
	private static final Pattern settingMatcher2 = Pattern.compile( "^\\s*(#?[a-z0-9\\.\\:_]+)\\s*=\\s*([^\"]*)\\s*$", Pattern.CASE_INSENSITIVE );

	private static KeyValuePair parse( String line )
	{
		Matcher matcher = settingMatcher1.matcher( line );
		if ( !matcher.matches() ) {
			matcher = settingMatcher2.matcher( line );
		}
		if ( !matcher.matches() ) {
			return null;
		}
		return new KeyValuePair( matcher.group( 1 ), matcher.group( 2 ) );

	}

	public ConfigEntry set( String key, String value, boolean replace )
	{
		if ( !replace && entries.containsKey( key ) )
			return null;
		ConfigEntry ce = new ConfigEntry( value );
		entries.put( key, ce );
		return ce;
	}

	public ConfigEntry set( String key, String value )
	{
		return set( key, value, true );
	}

	public ConfigEntry set( KeyValuePair entry )
	{
		return set( entry.key, entry.value );
	}

	public void remove( String key )
	{
		entries.remove( key );
	}

	public String get( String key )
	{
		ConfigEntry ce = entries.get( key );
		if ( ce == null )
			return null;
		return ce.value;
	}

	public String toString( boolean filteredRequired, boolean generatedRequired )
	{
		set( ".encoding", "UTF-8" ).filtered( true ).generated( true );
		StringBuilder sb = new StringBuilder( 300 );
		for ( Entry<String, ConfigEntry> entry : entries.entrySet() ) {
			ConfigEntry value = entry.getValue();
			if ( ( !filteredRequired || value.forFiltered ) && ( !generatedRequired || value.forGenerated ) ) {
				sb.append( entry.getKey() );
				sb.append( " = \"" );
				sb.append( value.getEscaped() );
				sb.append( "\"\n" );
			}
		}
		return sb.toString();
	}

	@Override
	public String toString()
	{
		return toString( false, false );
	}

	public static class ConfigEntry
	{
		private String value;
		private boolean forFiltered;
		private boolean forGenerated;

		public ConfigEntry( String value )
		{
			this.value = value;
		}

		public ConfigEntry filtered( boolean set )
		{
			this.forFiltered = set;
			return this;
		}

		public ConfigEntry generated( boolean set )
		{
			this.forGenerated = set;
			return this;
		}

		public String getEscaped()
		{
			String ret = value;
			if ( ret.contains( "|" ) ) {
				ret = ret.replace( "|", "|7C" );
			}
			if ( ret.contains( "\"" ) ) {
				ret = ret.replace( "\"", "|22" );
			}
			return ret;
		}

		public String getValue()
		{
			return value;
		}

		public void setValue( String value )
		{
			this.value = value;
		}
	}
}