diff options
author | Manuel Bentele | 2021-05-19 09:01:38 +0200 |
---|---|---|
committer | Manuel Bentele | 2021-05-19 09:01:38 +0200 |
commit | adc19701eafcd18a746a57dc0a610a614e1d80cf (patch) | |
tree | df9f27c6172684909ed1c5ffede2a46243322a8d /src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java | |
parent | Remove needless reference to internal/private Maven repository (diff) | |
parent | Implements XML schema validation for VirtualBox configurations (diff) | |
download | master-sync-shared-adc19701eafcd18a746a57dc0a610a614e1d80cf.tar.gz master-sync-shared-adc19701eafcd18a746a57dc0a610a614e1d80cf.tar.xz master-sync-shared-adc19701eafcd18a746a57dc0a610a614e1d80cf.zip |
Merge branch 'feature/qemu-integration'
Diffstat (limited to 'src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java')
-rw-r--r-- | src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java | 288 |
1 files changed, 288 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..24df02f --- /dev/null +++ b/src/main/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVmwareFileFormat.java @@ -0,0 +1,288 @@ +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.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 = Logger.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 ) { + if ( entry.key.equals( "virtualHW.version" ) || entry.key.equals( "ddb.virtualHWVersion" ) + // TODO: This is supposed to be case insensitive. + // Check if there are other consequences from lowercase entries in converted vmx files. + || 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 ); + 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 = 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; + } + } +} |