summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java')
-rw-r--r--src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java
new file mode 100644
index 0000000..356a034
--- /dev/null
+++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java
@@ -0,0 +1,284 @@
+package org.openslx.virtualization.configuration;
+
+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.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.openslx.util.Util;
+
+class KeyValuePair
+{
+ public final String key;
+ public final String value;
+
+ public KeyValuePair( String key, String value )
+ {
+ this.key = key;
+ this.value = value;
+ }
+}
+
+public class VirtualizationConfigurationVmwareFileFormat
+{
+
+ private static final Logger LOGGER = LogManager.getLogger( VirtualizationConfigurationVmwareFileFormat.class );
+
+ private Map<String, ConfigEntry> entries = new TreeMap<>( String.CASE_INSENSITIVE_ORDER );
+
+ public VirtualizationConfigurationVmwareFileFormat()
+ {
+ // (void)
+ }
+
+ public VirtualizationConfigurationVmwareFileFormat( File file ) throws IOException, VirtualizationConfigurationException
+ {
+ 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 VirtualizationConfigurationVmwareFileFormat( InputStream is ) throws IOException, VirtualizationConfigurationException
+ {
+ 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 VirtualizationConfigurationVmwareFileFormat( byte[] vmxContent, int length ) throws VirtualizationConfigurationException
+ {
+ init( vmxContent, length );
+ }
+
+ // function is used for both .vmx and .vmdk files
+ private void init( byte[] vmxContent, int length ) throws VirtualizationConfigurationException
+ {
+ try {
+ boolean isValid = false;
+ BufferedReader reader = getVmxReader( vmxContent, length );
+ String line;
+ while ( ( line = reader.readLine() ) != null ) {
+ KeyValuePair entry = parse( line );
+
+ if ( entry != null ) {
+ // TODO: This is supposed to be case insensitive.
+ // Check if there are other consequences from lowercase entries in converted vmx files.
+ if ( entry.key.equals( "virtualHW.version" ) || entry.key.equals( "ddb.virtualHWVersion" )
+ || entry.key.equals( "virtualhw.version" ) ) {
+ isValid = true;
+ }
+ set( entry.key, unescape( entry.value ) );
+ }
+ }
+ if ( !isValid ) {
+ throw new VirtualizationConfigurationException( "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 );
+ if ( cs == null )
+ cs = StandardCharsets.UTF_8; // YES BECAUSE THIS IS NOT VMX AND EVERYTHING IS SHIT
+ return new BufferedReader( new InputStreamReader( new ByteArrayInputStream( vmxContent, 0, length ), cs ) );
+ }
+
+ /**
+ * Get charset of config. Returns null if input doesn't look like a vmx file.
+ * @param vmxContent
+ * @param length
+ * @return
+ */
+ public static Charset getCharset( byte[] vmxContent, int length )
+ {
+ String csName = detectCharset( new ByteArrayInputStream( vmxContent, 0, length ) );
+ if ( csName == null )
+ return null;
+ 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 = ret.replace( "|7C", "|" );
+ }
+ return ret;
+ }
+
+ private static String detectCharset( InputStream is )
+ {
+ boolean isVmware = false;
+ 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;
+ }
+ if ( entry.key.equals( "virtualHW.version" ) || entry.key.equals( "memsize" ) || entry.key.equals( "displayName") ) {
+ isVmware = true;
+ }
+ }
+ } catch ( Exception e ) {
+ LOGGER.warn( "Could not detect charset, fallback to latin1", e );
+ }
+ if ( !isVmware )
+ return null;
+ // 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;
+ }
+
+ @Override
+ public String toString()
+ {
+ set( ".encoding", "UTF-8" );
+ StringBuilder sb = new StringBuilder( 300 );
+ for ( Entry<String, ConfigEntry> entry : entries.entrySet() ) {
+ ConfigEntry value = entry.getValue();
+ sb.append( entry.getKey() );
+ sb.append( " = \"" );
+ sb.append( value.getEscaped() );
+ sb.append( "\"\n" );
+ }
+ return sb.toString();
+ }
+
+ public static class ConfigEntry
+ {
+ private String value;
+
+ public ConfigEntry( String value )
+ {
+ this.value = value;
+ }
+
+ 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;
+ }
+ }
+}