From 001918e2d7e92693d32174b00f818861980dbbd1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 8 Jun 2019 12:34:03 -0700 Subject: [PATCH] GUACAMOLE-422: Allow arbitrary protocol versions to be represented. Remove API-level assumption that protocol capabilities will have a minimum base version and remain present from that point forward. --- .../protocol/ConfiguredGuacamoleSocket.java | 36 +++--- .../protocol/GuacamoleProtocolCapability.java | 22 ++-- .../protocol/GuacamoleProtocolVersion.java | 116 ++++-------------- 3 files changed, 58 insertions(+), 116 deletions(-) diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java index 450cef7f5..be755f433 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/ConfiguredGuacamoleSocket.java @@ -61,7 +61,7 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket { * it will be assumed that it operates at this version and certain features * may be unavailable. */ - private GuacamoleProtocolVersion protocol = + private GuacamoleProtocolVersion protocolVersion = GuacamoleProtocolVersion.VERSION_1_0_0; /** @@ -151,11 +151,21 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket { // Retrieve argument name String arg_name = arg_names.get(i); - // Check for protocol version as first argument - if (i == 0 && arg_name.startsWith("VERSION_")) { - protocol = GuacamoleProtocolVersion.getVersion(arg_name); - arg_values[i] = protocol.toString(); - continue; + // Check for valid protocol version as first argument + if (i == 0) { + GuacamoleProtocolVersion version = GuacamoleProtocolVersion.parseVersion(arg_name); + if (version != null) { + + // Use the lowest common version supported + if (version.atLeast(GuacamoleProtocolVersion.LATEST)) + version = GuacamoleProtocolVersion.LATEST; + + // Respond with the version selected + arg_values[i] = version.toString(); + protocolVersion = version; + continue; + + } } // Get defined value for name @@ -200,17 +210,11 @@ public class ConfiguredGuacamoleSocket implements GuacamoleSocket { info.getImageMimetypes().toArray(new String[0]) )); - // Check for support for timezone handshake - if (protocol.isSupported(GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE)) { - // Send client timezone, if available + // Send client timezone, if supported and available + if (GuacamoleProtocolCapability.TIMEZONE_HANDSHAKE.isSupported(protocolVersion)) { String timezone = info.getTimezone(); - if (timezone != null) { - writer.writeInstruction( - new GuacamoleInstruction( - "timezone", - info.getTimezone() - )); - } + if (timezone != null) + writer.writeInstruction(new GuacamoleInstruction("timezone", info.getTimezone())); } // Send args diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolCapability.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolCapability.java index 6d4bdd169..1347bf534 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolCapability.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolCapability.java @@ -59,19 +59,23 @@ public enum GuacamoleProtocolCapability { * The minimum required protocol version for supporting the * capability. */ - GuacamoleProtocolCapability(GuacamoleProtocolVersion version) { + private GuacamoleProtocolCapability(GuacamoleProtocolVersion version) { this.version = version; } - + /** - * Returns the minimum protocol version required to support this - * capability. - * + * Returns whether this capability is supported in the given Guacamole + * protocol version. + * + * @param version + * The Guacamole protocol version to check. + * * @return - * The minimum protocol version required to support this capability. + * true if this capability is supported by the given protocol version, + * false otherwise. */ - public GuacamoleProtocolVersion getVersion() { - return version; + public boolean isSupported(GuacamoleProtocolVersion version) { + return version.atLeast(this.version); } - + } diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java index 3b38c427a..a3ee25f0c 100644 --- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java +++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java @@ -23,11 +23,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * An enum that defines the available Guacamole protocol versions that can be - * used between guacd and clients, and provides convenience methods for parsing - * and comparing versions. + * Representation of a Guacamole protocol version. Convenience methods are + * provided for parsing and comparing versions, as is necessary when + * determining the version of the Guacamole protocol common to guacd and a + * client. */ -public enum GuacamoleProtocolVersion { +public class GuacamoleProtocolVersion { /** * Protocol version 1.0.0 and older. Any client that doesn't explicitly @@ -36,14 +37,20 @@ public enum GuacamoleProtocolVersion { * lacks support for certain protocol-related features introduced in later * versions. */ - VERSION_1_0_0(1, 0, 0), + public static final GuacamoleProtocolVersion VERSION_1_0_0 = new GuacamoleProtocolVersion(1, 0, 0); /** * Protocol version 1.1.0, which introduces Client-Server version * detection, arbitrary handshake instruction order, and support * for passing the client timezone to the server during the handshake. */ - VERSION_1_1_0(1, 1, 0); + public static final GuacamoleProtocolVersion VERSION_1_1_0 = new GuacamoleProtocolVersion(1, 1, 0); + + /** + * The most recent version of the Guacamole protocol at the time this + * version of GuacamoleProtocolVersion was built. + */ + public static final GuacamoleProtocolVersion LATEST = VERSION_1_1_0; /** * A regular expression that matches the VERSION_X_Y_Z pattern, where @@ -83,7 +90,7 @@ public enum GuacamoleProtocolVersion { * @param patch * The integer representation of the patch version component. */ - GuacamoleProtocolVersion(int major, int minor, int patch) { + public GuacamoleProtocolVersion(int major, int minor, int patch) { this.major = major; this.minor = minor; this.patch = patch; @@ -131,7 +138,7 @@ public enum GuacamoleProtocolVersion { * @return * True if this object is greater than or equal to the other version. */ - private boolean atLeast(GuacamoleProtocolVersion otherVersion) { + public boolean atLeast(GuacamoleProtocolVersion otherVersion) { // If major is not the same, return inequality if (major != otherVersion.getMajor()) @@ -146,39 +153,6 @@ public enum GuacamoleProtocolVersion { } - /** - * Compare this version with the major, minor, and patch components - * provided to the method, and determine if this version is compatible - * with the provided version, returning a boolean true if it is compatible, - * otherwise false. This version is compatible with the version specified - * by the provided components if the major, minor, and patch components - * are equivalent or less than those provided. - * - * @param major - * The major version component to compare for compatibility. - * - * @param minor - * The minor version component to compare for compatibility. - * - * @param patch - * The patch version component to compare for compatibility. - * - * @return - * True if this version is compatibility with the version components - * provided, otherwise false. - */ - private boolean isCompatible(int major, int minor, int patch) { - - if (this.major != major) - return this.major < major; - - if (this.minor != minor) - return this.minor < minor; - - return this.patch <= patch; - - } - /** * Parse the String format of the version provided and return the * the enum value matching that version. If no value is provided, return @@ -191,60 +165,20 @@ public enum GuacamoleProtocolVersion { * The enum value that matches the specified version, VERSION_1_0_0 * if no match is found, or null if no comparison version is provided. */ - public static GuacamoleProtocolVersion getVersion(String version) { - - // If nothing is passed in, return null - if (version == null || version.isEmpty()) - return null; - - // Check the string against the pattern matcher + public static GuacamoleProtocolVersion parseVersion(String version) { + + // Validate format of version string Matcher versionMatcher = VERSION_PATTERN.matcher(version); - - // If there is no RegEx match, return null if (!versionMatcher.matches()) return null; - - try { - // Try the valueOf function - return valueOf(version); - - } - - // If nothing matches, find the closest compatible version. - catch (IllegalArgumentException e) { - int myMajor = Integer.parseInt(versionMatcher.group(1)); - int myMinor = Integer.parseInt(versionMatcher.group(2)); - int myPatch = Integer.parseInt(versionMatcher.group(3)); - - GuacamoleProtocolVersion myVersion = VERSION_1_0_0; - - // Loop through possible versions, grabbing the latest compatible - for (GuacamoleProtocolVersion v : values()) { - if (v.isCompatible(myMajor, myMinor, myPatch)) - myVersion = v; - } - - return myVersion; - } - - } - - /** - * Returns true if the specified capability is supported in the current - * protocol version, otherwise false. - * - * @param capability - * The protocol capability that is being checked for support. - * - * @return - * True if the capability is supported in the current version, - * otherwise false. - */ - public boolean isSupported(GuacamoleProtocolCapability capability) { - - return atLeast(capability.getVersion()); - + // Parse version number from version string + return new GuacamoleProtocolVersion( + Integer.parseInt(versionMatcher.group(1)), + Integer.parseInt(versionMatcher.group(2)), + Integer.parseInt(versionMatcher.group(3)) + ); + } }