1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 package org.apache.guacamole.protocol;
21 
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24 
25 /**
26  * Representation of a Guacamole protocol version. Convenience methods are
27  * provided for parsing and comparing versions, as is necessary when
28  * determining the version of the Guacamole protocol common to guacd and a
29  * client.
30  */
31 public class GuacamoleProtocolVersion {
32 
33     /**
34      * Protocol version 1.0.0 and older.  Any client that doesn't explicitly
35      * set the protocol version will negotiate down to this protocol version.
36      * This requires that handshake instructions be ordered correctly, and
37      * lacks support for certain protocol-related features introduced in later
38      * versions.
39      */
40     public static final GuacamoleProtocolVersion VERSION_1_0_0 = new GuacamoleProtocolVersion(1, 0, 0);
41 
42     /**
43      * Protocol version 1.1.0, which introduces Client-Server version
44      * detection, arbitrary handshake instruction order, and support
45      * for passing the client timezone to the server during the handshake.
46      */
47     public static final GuacamoleProtocolVersion VERSION_1_1_0 = new GuacamoleProtocolVersion(1, 1, 0);
48 
49     /**
50      * Protocol version 1.3.0, which introduces the "required" instruction
51      * allowing the server to explicitly request connection parameters from the
52      * client.
53      */
54     public static final GuacamoleProtocolVersion VERSION_1_3_0 = new GuacamoleProtocolVersion(1, 3, 0);
55 
56     /**
57      * The most recent version of the Guacamole protocol at the time this
58      * version of GuacamoleProtocolVersion was built.
59      */
60     public static final GuacamoleProtocolVersion LATEST = VERSION_1_3_0;
61 
62     /**
63      * A regular expression that matches the VERSION_X_Y_Z pattern, where
64      * X is the major version component, Y is the minor version component,
65      * and Z is the patch version component.  This expression puts each of
66      * the version components in their own group so that they can be easily
67      * used later.
68      */
69     private static final Pattern VERSION_PATTERN =
70             Pattern.compile("^VERSION_([0-9]+)_([0-9]+)_([0-9]+)$");
71 
72     /**
73      * The major version component of the protocol version.
74      */
75     private final int major;
76 
77     /**
78      * The minor version component of the protocol version.
79      */
80     private final int minor;
81 
82     /**
83      * The patch version component of the protocol version.
84      */
85     private final int patch;
86 
87     /**
88      * Generate a new GuacamoleProtocolVersion object with the given
89      * major version, minor version, and patch version.
90      *
91      * @param major
92      *     The integer representation of the major version component.
93      *
94      * @param minor
95      *     The integer representation of the minor version component.
96      *
97      * @param patch
98      *     The integer representation of the patch version component.
99      */
GuacamoleProtocolVersion(int major, int minor, int patch)100     public GuacamoleProtocolVersion(int major, int minor, int patch) {
101         this.major = major;
102         this.minor = minor;
103         this.patch = patch;
104     }
105 
106     /**
107      * Return the major version component of the protocol version.
108      *
109      * @return
110      *     The integer major version component.
111      */
getMajor()112     public int getMajor() {
113         return major;
114     }
115 
116     /**
117      * Return the minor version component of the protocol version.
118      *
119      * @return
120      *     The integer minor version component.
121      */
getMinor()122     public int getMinor() {
123         return minor;
124     }
125 
126     /**
127      * Return the patch version component of the protocol version.
128      *
129      * @return
130      *     The integer patch version component.
131      */
getPatch()132     public int getPatch() {
133         return patch;
134     }
135 
136     /**
137      * Returns whether this GuacamoleProtocolVersion is at least as recent as
138      * (greater than or equal to) the given version.
139      *
140      * @param otherVersion
141      *     The version to which this GuacamoleProtocolVersion should be compared.
142      *
143      * @return
144      *     true if this object is at least as recent as the given version,
145      *     false if the given version is newer.
146      */
atLeast(GuacamoleProtocolVersion otherVersion)147     public boolean atLeast(GuacamoleProtocolVersion otherVersion) {
148 
149         // If major is not the same, return inequality
150         if (major != otherVersion.getMajor())
151             return this.major > otherVersion.getMajor();
152 
153         // Major is the same, but minor is not, return minor inequality
154         if (minor != otherVersion.getMinor())
155             return this.minor > otherVersion.getMinor();
156 
157         // Major and minor are equal, so return patch inequality
158         return patch >= otherVersion.getPatch();
159 
160     }
161 
162     /**
163      * Parse the String format of the version provided and return the
164      * the enum value matching that version.  If no value is provided, return
165      * null.
166      *
167      * @param version
168      *     The String format of the version to parse.
169      *
170      * @return
171      *     The enum value that matches the specified version, VERSION_1_0_0
172      *     if no match is found, or null if no comparison version is provided.
173      */
parseVersion(String version)174     public static GuacamoleProtocolVersion parseVersion(String version) {
175 
176         // Validate format of version string
177         Matcher versionMatcher = VERSION_PATTERN.matcher(version);
178         if (!versionMatcher.matches())
179             return null;
180 
181         // Parse version number from version string
182         return new GuacamoleProtocolVersion(
183             Integer.parseInt(versionMatcher.group(1)),
184             Integer.parseInt(versionMatcher.group(2)),
185             Integer.parseInt(versionMatcher.group(3))
186         );
187 
188     }
189 
190     @Override
hashCode()191     public int hashCode() {
192         int hash = 7;
193         hash = 61 * hash + this.major;
194         hash = 61 * hash + this.minor;
195         hash = 61 * hash + this.patch;
196         return hash;
197     }
198 
199     @Override
equals(Object obj)200     public boolean equals(Object obj) {
201 
202         if (obj == null || !(obj instanceof GuacamoleProtocolVersion))
203             return false;
204 
205         // Versions are equal if all major/minor/patch components are identical
206         final GuacamoleProtocolVersion otherVersion = (GuacamoleProtocolVersion) obj;
207         return this.major == otherVersion.getMajor()
208             && this.minor == otherVersion.getMinor()
209             && this.patch == otherVersion.getPatch();
210 
211     }
212 
213     @Override
toString()214     public String toString() {
215         return "VERSION_" + getMajor() + "_" + getMinor() + "_" + getPatch();
216     }
217 
218 }
219