GUACAMOLE-354: Merge translation and definition for Swiss German keymap option.
diff --git a/doc/guacamole-example/pom.xml b/doc/guacamole-example/pom.xml
index f2f56f3..bb2ea68 100644
--- a/doc/guacamole-example/pom.xml
+++ b/doc/guacamole-example/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-example</artifactId>
<packaging>war</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-example</name>
<url>http://guacamole.apache.org/</url>
@@ -106,7 +106,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>compile</scope>
</dependency>
@@ -114,7 +114,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>
diff --git a/doc/guacamole-playback-example/pom.xml b/doc/guacamole-playback-example/pom.xml
index 1b31374..f7055d3 100644
--- a/doc/guacamole-playback-example/pom.xml
+++ b/doc/guacamole-playback-example/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-playback-example</artifactId>
<packaging>war</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-playback-example</name>
<url>http://guacamole.apache.org/</url>
@@ -88,7 +88,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>
diff --git a/extensions/guacamole-auth-cas/pom.xml b/extensions/guacamole-auth-cas/pom.xml
index ae8bd67..69ecab5 100644
--- a/extensions/guacamole-auth-cas/pom.xml
+++ b/extensions/guacamole-auth-cas/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-cas</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-cas</name>
<url>http://guacamole.apache.org/</url>
@@ -210,7 +210,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
@@ -218,7 +218,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
index fbf8c09..d037fbb 100644
--- a/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "CAS Authentication Extension",
"namespace" : "guac-cas",
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
index 55de752..330f76e 100644
--- a/extensions/guacamole-auth-duo/pom.xml
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-duo</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-duo</name>
<url>http://guacamole.apache.org/</url>
@@ -213,7 +213,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
index 4c87382..6de72fa 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "Duo TFA Authentication Backend",
"namespace" : "duo",
diff --git a/extensions/guacamole-auth-header/pom.xml b/extensions/guacamole-auth-header/pom.xml
index 9c07bab..cf65658 100644
--- a/extensions/guacamole-auth-header/pom.xml
+++ b/extensions/guacamole-auth-header/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-header</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-header</name>
<url>http://guacamole.apache.org/</url>
@@ -130,7 +130,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-header/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-header/src/main/resources/guac-manifest.json
index c976eef..3c71028 100644
--- a/extensions/guacamole-auth-header/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-header/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "HTTP Header Authentication Extension",
"namespace" : "guac-header",
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
index a3b7f92..fdc8003 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml
@@ -36,7 +36,7 @@
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<relativePath>../../</relativePath>
</parent>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
index 68e2a47..ff605b9 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderService.java
@@ -96,6 +96,7 @@
// Retrieve user account for already-authenticated user
ModeledUser user = userService.retrieveUser(authenticationProvider, authenticatedUser);
+ ModeledUserContext context = userContextProvider.get();
if (user != null && !user.isDisabled()) {
// Enforce applicable account restrictions
@@ -118,24 +119,23 @@
userService.resetExpiredPassword(user, authenticatedUser.getCredentials());
}
- // Return all data associated with the authenticated user
- ModeledUserContext context = userContextProvider.get();
- context.init(user.getCurrentUser());
- return context;
-
+ }
+
+ // If no user account is found, and database-specific account
+ // restrictions do not apply, get an empty user.
+ else if (!databaseRestrictionsApplicable) {
+ user = userService.retrieveSkeletonUser(authenticationProvider, authenticatedUser);
}
// Veto authentication result only if database-specific account
// restrictions apply in this situation
- if (databaseRestrictionsApplicable)
+ else
throw new GuacamoleInvalidCredentialsException("Invalid login",
CredentialsInfo.USERNAME_PASSWORD);
-
- // There is no data to be returned for the user, either because they do
- // not exist or because restrictions prevent their data from being
- // retrieved, but no restrictions apply which should prevent the user
- // from authenticating entirely
- return null;
+
+ // Initialize the UserContext with the user account and return it.
+ context.init(user.getCurrentUser());
+ return context;
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
index 828b05e..5778ad0 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledAuthenticatedUser.java
@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.jdbc.user;
+import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -168,5 +169,11 @@
public void setIdentifier(String identifier) {
user.setIdentifier(identifier);
}
+
+ @Override
+ public Set<String> getEffectiveUserGroups() {
+ return Sets.union(user.getEffectiveUserGroups(),
+ super.getEffectiveUserGroups());
+ }
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java
index 194a26d..3d441d6 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java
@@ -127,6 +127,17 @@
public UserModel() {
super(EntityType.USER);
}
+
+ /**
+ * Creates a new user having the provided identifier.
+ *
+ * @param identifier
+ * The identifier of the new user.
+ */
+ public UserModel(String identifier) {
+ super(EntityType.USER);
+ super.setIdentifier(identifier);
+ }
/**
* Returns the hash of this user's password and password salt. This may be
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
index 60bd1e1..0cfe900 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java
@@ -423,6 +423,43 @@
return user;
}
+
+ /**
+ * Generates an empty (skeleton) user corresponding to the given
+ * AuthenticatedUser. The user will not be stored in the database, and
+ * will only be available in-memory during the time the session is
+ * active.
+ *
+ * @param authenticationProvider
+ * The AuthenticationProvider on behalf of which the user is being
+ * retrieved.
+ *
+ * @param authenticatedUser
+ * The AuthenticatedUser to generate the skeleton account for.
+ *
+ * @return
+ * The empty ModeledUser which corresponds to the given
+ * AuthenticatedUser.
+ *
+ * @throws GuacamoleException
+ * If a ModeledUser object for the user corresponding to the given
+ * AuthenticatedUser cannot be created.
+ */
+ public ModeledUser retrieveSkeletonUser(AuthenticationProvider authenticationProvider,
+ AuthenticatedUser authenticatedUser) throws GuacamoleException {
+
+ // Set up an empty user model
+ ModeledUser user = getObjectInstance(null,
+ new UserModel(authenticatedUser.getIdentifier()));
+
+ // Create user object, and configure cyclic reference
+ user.setCurrentUser(new ModeledAuthenticatedUser(authenticatedUser,
+ authenticationProvider, user));
+
+ // Return the new user.
+ return user;
+
+ }
/**
* Resets the password of the given user to the new password specified via
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/pom.xml
index a204c8e..ddb57a9 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/pom.xml
@@ -36,7 +36,7 @@
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<relativePath>../../</relativePath>
</parent>
@@ -99,21 +99,21 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-mysql</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
<!-- PostgreSQL Authentication Extension -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-postgresql</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
<!-- SQL Server Authentication Extension -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-sqlserver</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
</dependencies>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml
index b438735..8a7d68a 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml
@@ -36,7 +36,7 @@
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<relativePath>../../</relativePath>
</parent>
@@ -120,7 +120,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-base</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
</dependencies>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json
index 7fb613c..d80b594 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "MySQL Authentication",
"namespace" : "guac-mysql",
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
index 21efb99..a292511 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
@@ -73,27 +73,31 @@
JOIN guacamole_user_group_member ON guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
WHERE
guacamole_user_group.disabled = false
- AND (
- guacamole_user_group_member.member_entity_id = #{entity.entityID}
- <if test="!effectiveGroups.isEmpty()">
- OR guacamole_user_group_member.member_entity_id IN (
- SELECT entity_id FROM guacamole_entity
- WHERE type = 'USER_GROUP' AND name IN
- <foreach collection="effectiveGroups" item="effectiveGroup"
- open="(" separator="," close=")">
- #{effectiveGroup,jdbcType=VARCHAR}
- </foreach>
- )
- OR guacamole_user_group.entity_id IN (
- SELECT entity_id FROM guacamole_entity
- WHERE type = 'USER_GROUP' AND name IN
- <foreach collection="effectiveGroups" item="effectiveGroup"
- open="(" separator="," close=")">
- #{effectiveGroup,jdbcType=VARCHAR}
- </foreach>
- )
- </if>
- )
+ AND guacamole_user_group_member.member_entity_id = #{entity.entityID}
+ <if test="!effectiveGroups.isEmpty()">
+ UNION SELECT
+ guacamole_entity.name
+ FROM guacamole_user_group
+ JOIN guacamole_entity ON guacamole_user_group.entity_id = guacamole_entity.entity_id
+ JOIN guacamole_user_group_member ON guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
+ JOIN guacamole_entity member_entity ON guacamole_user_group_member.member_entity_id = member_entity.entity_id
+ WHERE
+ guacamole_user_group.disabled = false
+ AND member_entity.type = 'USER_GROUP' AND member_entity.name IN
+ <foreach collection="effectiveGroups" item="effectiveGroup"
+ open="(" separator="," close=")">
+ #{effectiveGroup,jdbcType=VARCHAR}
+ </foreach>
+ UNION SELECT
+ guacamole_entity.name
+ FROM guacamole_user_group
+ JOIN guacamole_entity ON guacamole_user_group.entity_id = guacamole_entity.entity_id
+ WHERE type = 'USER_GROUP' AND name IN
+ <foreach collection="effectiveGroups" item="effectiveGroup"
+ open="(" separator="," close=")">
+ #{effectiveGroup,jdbcType=VARCHAR}
+ </foreach>
+ </if>
</if>
<if test="recursive">
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml
index bfe4a3c..340eba7 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/pom.xml
@@ -36,7 +36,7 @@
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<relativePath>../../</relativePath>
</parent>
@@ -120,7 +120,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-base</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
</dependencies>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json
index b4047bc..6b2d17c 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "PostgreSQL Authentication",
"namespace" : "guac-postgresql",
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
index 0a8468b..55455db 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/pom.xml
@@ -36,7 +36,7 @@
<parent>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<relativePath>../../</relativePath>
</parent>
@@ -120,7 +120,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-base</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
</dependencies>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
index c7a0caa..79286f1 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "SQLServer Authentication",
"namespace" : "guac-sqlserver",
diff --git a/extensions/guacamole-auth-jdbc/pom.xml b/extensions/guacamole-auth-jdbc/pom.xml
index 9aee2df..1325949 100644
--- a/extensions/guacamole-auth-jdbc/pom.xml
+++ b/extensions/guacamole-auth-jdbc/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
<packaging>pom</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-jdbc</name>
<url>http://guacamole.apache.org/</url>
@@ -81,7 +81,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-ldap/pom.xml b/extensions/guacamole-auth-ldap/pom.xml
index 856f630..79560f3 100644
--- a/extensions/guacamole-auth-ldap/pom.xml
+++ b/extensions/guacamole-auth-ldap/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-ldap</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-ldap</name>
<url>http://guacamole.apache.org/</url>
@@ -137,7 +137,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java
index 6a96d5b..2f2b674 100644
--- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java
+++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java
@@ -178,7 +178,7 @@
// Store connection using cn for both identifier and name
String name = cn.getStringValue();
- Connection connection = new SimpleConnection(name, name, config);
+ Connection connection = new SimpleConnection(name, name, config, true);
connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP);
// Inject LDAP-specific tokens only if LDAP handled user
diff --git a/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json
index 2d4d090..7002388 100644
--- a/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-ldap/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "LDAP Authentication",
"namespace" : "guac-ldap",
diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml
index 63ecd51..967ad03 100644
--- a/extensions/guacamole-auth-openid/pom.xml
+++ b/extensions/guacamole-auth-openid/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-openid</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-openid</name>
<url>http://guacamole.apache.org/</url>
@@ -210,7 +210,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json
index 91d33f1..4bac126 100644
--- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "OpenID Authentication Extension",
"namespace" : "guac-openid",
diff --git a/extensions/guacamole-auth-quickconnect/pom.xml b/extensions/guacamole-auth-quickconnect/pom.xml
index 8acf5f6..df290b8 100644
--- a/extensions/guacamole-auth-quickconnect/pom.xml
+++ b/extensions/guacamole-auth-quickconnect/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-quickconnect</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-quickconnect</name>
<url>http://guacamole.apache.org/</url>
@@ -188,7 +188,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json
index 7f1e4da..10c4f8a 100644
--- a/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json
@@ -1,5 +1,5 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "Adhoc Guacamole Connections",
"namespace" : "quickconnect",
diff --git a/extensions/guacamole-auth-radius/pom.xml b/extensions/guacamole-auth-radius/pom.xml
index d07a60a..73c332a 100644
--- a/extensions/guacamole-auth-radius/pom.xml
+++ b/extensions/guacamole-auth-radius/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-radius</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-radius</name>
<url>http://guacamole.apache.org/</url>
@@ -178,7 +178,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
@@ -186,7 +186,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json
index 55a3a0c..ac1116b 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "RADIUS Authentication Backend",
"namespace" : "radius",
diff --git a/extensions/guacamole-auth-totp/pom.xml b/extensions/guacamole-auth-totp/pom.xml
index d34db07..11199df 100644
--- a/extensions/guacamole-auth-totp/pom.xml
+++ b/extensions/guacamole-auth-totp/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-auth-totp</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-auth-totp</name>
<url>http://guacamole.incubator.apache.org/</url>
@@ -217,7 +217,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
diff --git a/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json
index fd62e14..31dda60 100644
--- a/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json
@@ -1,6 +1,6 @@
{
- "guacamoleVersion" : "1.0.0",
+ "guacamoleVersion" : "1.1.0",
"name" : "TOTP TFA Authentication Backend",
"namespace" : "totp",
diff --git a/guacamole-common-js/pom.xml b/guacamole-common-js/pom.xml
index cf9c45d..e94271b 100644
--- a/guacamole-common-js/pom.xml
+++ b/guacamole-common-js/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
<packaging>pom</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-common-js</name>
<url>http://guacamole.apache.org/</url>
diff --git a/guacamole-common-js/src/main/webapp/modules/Version.js b/guacamole-common-js/src/main/webapp/modules/Version.js
index 737d8d2..9131165 100644
--- a/guacamole-common-js/src/main/webapp/modules/Version.js
+++ b/guacamole-common-js/src/main/webapp/modules/Version.js
@@ -27,4 +27,4 @@
*
* @type {String}
*/
-Guacamole.API_VERSION = "1.0.0";
+Guacamole.API_VERSION = "1.1.0";
diff --git a/guacamole-common/pom.xml b/guacamole-common/pom.xml
index 219d694..2b4f353 100644
--- a/guacamole-common/pom.xml
+++ b/guacamole-common/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-common</name>
<url>http://guacamole.apache.org/</url>
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 cf43b68..fe4efca 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
@@ -19,7 +19,6 @@
package org.apache.guacamole.protocol;
-
import java.util.List;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleServerException;
@@ -57,6 +56,15 @@
private String id;
/**
+ * The protocol version that will be used to communicate with guacd. The
+ * default is 1.0.0, and, if the server does not provide a specific version
+ * it will be assumed that it operates at this version and certain features
+ * may be unavailable.
+ */
+ private GuacamoleProtocolVersion protocolVersion =
+ GuacamoleProtocolVersion.VERSION_1_0_0;
+
+ /**
* Waits for the instruction having the given opcode, returning that
* instruction once it has been read. If the instruction is never read,
* an exception is thrown.
@@ -142,6 +150,23 @@
// Retrieve argument name
String arg_name = arg_names.get(i);
+
+ // 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
String value = config.getParameter(arg_name);
@@ -184,6 +209,13 @@
"image",
info.getImageMimetypes().toArray(new String[0])
));
+
+ // 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()));
+ }
// Send args
writer.writeInstruction(new GuacamoleInstruction("connect", arg_values));
@@ -221,6 +253,20 @@
return id;
}
+ /**
+ * Returns the version of the Guacamole protocol associated with the
+ * Guacamole connection negotiated by this ConfiguredGuacamoleSocket. This
+ * version is the lowest version common to both ConfiguredGuacamoleSocket
+ * and the relevant Guacamole proxy instance (guacd).
+ *
+ * @return
+ * The protocol version that this ConfiguredGuacamoleSocket will use to
+ * communicate with guacd.
+ */
+ public GuacamoleProtocolVersion getProtocolVersion() {
+ return protocolVersion;
+ }
+
@Override
public GuacamoleWriter getWriter() {
return socket.getWriter();
diff --git a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleClientInformation.java b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleClientInformation.java
index d90d05d..6d54a2f 100644
--- a/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleClientInformation.java
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleClientInformation.java
@@ -58,6 +58,11 @@
* The list of image mimetypes reported by the client to be supported.
*/
private final List<String> imageMimetypes = new ArrayList<String>();
+
+ /**
+ * The timezone reported by the client.
+ */
+ private String timezone;
/**
* Returns the optimal screen width requested by the client, in pixels.
@@ -144,5 +149,31 @@
public List<String> getImageMimetypes() {
return imageMimetypes;
}
+
+ /**
+ * Return the timezone as reported by the client, or null if the timezone
+ * is not set. Valid timezones are specified in IANA zone key format,
+ * also known as Olson time zone database or TZ Database.
+ *
+ * @return
+ * A string value of the timezone reported by the client.
+ */
+ public String getTimezone() {
+ return timezone;
+ }
+
+ /**
+ * Set the string value of the timezone, or null if the timezone will not
+ * be provided by the client. Valid timezones are specified in IANA zone
+ * key format (aka Olson time zone database or tz database).
+ *
+ * @param timezone
+ * The string value of the timezone reported by the client, in tz
+ * database format, or null if the timezone is not provided by the
+ * client.
+ */
+ public void setTimezone(String timezone) {
+ this.timezone = timezone;
+ }
}
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
new file mode 100644
index 0000000..79f73f8
--- /dev/null
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolCapability.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.protocol;
+
+/**
+ * Capabilities which may not be present in all versions of the Guacamole
+ * protocol.
+ */
+public enum GuacamoleProtocolCapability {
+
+ /**
+ * The protocol does not require handshake instructions to be sent in a
+ * specific order, nor that all handshake instructions be sent. Arbitrary
+ * handshake order was introduced in
+ * {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
+ */
+ ARBITRARY_HANDSHAKE_ORDER(GuacamoleProtocolVersion.VERSION_1_1_0),
+
+ /**
+ * Negotiation of Guacamole protocol version between client and server
+ * during the protocol handshake. The ability to negotiate protocol
+ * versions was introduced in
+ * {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
+ */
+ PROTOCOL_VERSION_DETECTION(GuacamoleProtocolVersion.VERSION_1_1_0),
+
+ /**
+ * Support for the "timezone" handshake instruction. The "timezone"
+ * instruction allows the client to request that the server forward their
+ * local timezone for use within the remote desktop session. Support for
+ * forwarding the client timezone was introduced in
+ * {@link GuacamoleProtocolVersion#VERSION_1_1_0}.
+ */
+ TIMEZONE_HANDSHAKE(GuacamoleProtocolVersion.VERSION_1_1_0);
+
+ /**
+ * The minimum protocol version required to support this capability.
+ */
+ private final GuacamoleProtocolVersion version;
+
+ /**
+ * Create a new enum value with the given protocol version as the minimum
+ * required to support the capability.
+ *
+ * @param version
+ * The minimum required protocol version for supporting the
+ * capability.
+ */
+ private GuacamoleProtocolCapability(GuacamoleProtocolVersion version) {
+ this.version = version;
+ }
+
+ /**
+ * Returns whether this capability is supported in the given Guacamole
+ * protocol version.
+ *
+ * @param version
+ * The Guacamole protocol version to check.
+ *
+ * @return
+ * true if this capability is supported by the given protocol version,
+ * false otherwise.
+ */
+ 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
new file mode 100644
index 0000000..c1d50ba
--- /dev/null
+++ b/guacamole-common/src/main/java/org/apache/guacamole/protocol/GuacamoleProtocolVersion.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.protocol;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 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 class GuacamoleProtocolVersion {
+
+ /**
+ * Protocol version 1.0.0 and older. Any client that doesn't explicitly
+ * set the protocol version will negotiate down to this protocol version.
+ * This requires that handshake instructions be ordered correctly, and
+ * lacks support for certain protocol-related features introduced in later
+ * versions.
+ */
+ 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.
+ */
+ 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
+ * X is the major version component, Y is the minor version component,
+ * and Z is the patch version component. This expression puts each of
+ * the version components in their own group so that they can be easily
+ * used later.
+ */
+ private static final Pattern VERSION_PATTERN =
+ Pattern.compile("^VERSION_([0-9]+)_([0-9]+)_([0-9]+)$");
+
+ /**
+ * The major version component of the protocol version.
+ */
+ private final int major;
+
+ /**
+ * The minor version component of the protocol version.
+ */
+ private final int minor;
+
+ /**
+ * The patch version component of the protocol version.
+ */
+ private final int patch;
+
+ /**
+ * Generate a new GuacamoleProtocolVersion object with the given
+ * major version, minor version, and patch version.
+ *
+ * @param major
+ * The integer representation of the major version component.
+ *
+ * @param minor
+ * The integer representation of the minor version component.
+ *
+ * @param patch
+ * The integer representation of the patch version component.
+ */
+ public GuacamoleProtocolVersion(int major, int minor, int patch) {
+ this.major = major;
+ this.minor = minor;
+ this.patch = patch;
+ }
+
+ /**
+ * Return the major version component of the protocol version.
+ *
+ * @return
+ * The integer major version component.
+ */
+ public int getMajor() {
+ return major;
+ }
+
+ /**
+ * Return the minor version component of the protocol version.
+ *
+ * @return
+ * The integer minor version component.
+ */
+ public int getMinor() {
+ return minor;
+ }
+
+ /**
+ * Return the patch version component of the protocol version.
+ *
+ * @return
+ * The integer patch version component.
+ */
+ public int getPatch() {
+ return patch;
+ }
+
+ /**
+ * Returns whether this GuacamoleProtocolVersion is at least as recent as
+ * (greater than or equal to) the given version.
+ *
+ * @param otherVersion
+ * The version to which this GuacamoleProtocolVersion should be compared.
+ *
+ * @return
+ * true if this object is at least as recent as the given version,
+ * false if the given version is newer.
+ */
+ public boolean atLeast(GuacamoleProtocolVersion otherVersion) {
+
+ // If major is not the same, return inequality
+ if (major != otherVersion.getMajor())
+ return this.major > otherVersion.getMajor();
+
+ // Major is the same, but minor is not, return minor inequality
+ if (minor != otherVersion.getMinor())
+ return this.minor > otherVersion.getMinor();
+
+ // Major and minor are equal, so return patch inequality
+ return patch >= otherVersion.getPatch();
+
+ }
+
+ /**
+ * Parse the String format of the version provided and return the
+ * the enum value matching that version. If no value is provided, return
+ * null.
+ *
+ * @param version
+ * The String format of the version to parse.
+ *
+ * @return
+ * 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 parseVersion(String version) {
+
+ // Validate format of version string
+ Matcher versionMatcher = VERSION_PATTERN.matcher(version);
+ if (!versionMatcher.matches())
+ return null;
+
+ // Parse version number from version string
+ return new GuacamoleProtocolVersion(
+ Integer.parseInt(versionMatcher.group(1)),
+ Integer.parseInt(versionMatcher.group(2)),
+ Integer.parseInt(versionMatcher.group(3))
+ );
+
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 61 * hash + this.major;
+ hash = 61 * hash + this.minor;
+ hash = 61 * hash + this.patch;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (obj == null || !(obj instanceof GuacamoleProtocolVersion))
+ return false;
+
+ // Versions are equal if all major/minor/patch components are identical
+ final GuacamoleProtocolVersion otherVersion = (GuacamoleProtocolVersion) obj;
+ return this.major == otherVersion.getMajor()
+ && this.minor == otherVersion.getMinor()
+ && this.patch == otherVersion.getPatch();
+
+ }
+
+ @Override
+ public String toString() {
+ return "VERSION_" + getMajor() + "_" + getMinor() + "_" + getPatch();
+ }
+
+}
diff --git a/guacamole-common/src/test/java/org/apache/guacamole/protocol/GuacamoleProtocolVersionTest.java b/guacamole-common/src/test/java/org/apache/guacamole/protocol/GuacamoleProtocolVersionTest.java
new file mode 100644
index 0000000..d5082b5
--- /dev/null
+++ b/guacamole-common/src/test/java/org/apache/guacamole/protocol/GuacamoleProtocolVersionTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.protocol;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit test for GuacamoleProtocolVersion. Verifies that Guacamole protocol
+ * version string parsing works as required.
+ */
+public class GuacamoleProtocolVersionTest {
+
+ /**
+ * Verifies that valid version strings are parsed successfully.
+ */
+ @Test
+ public void testValidVersionParse() {
+ GuacamoleProtocolVersion version = GuacamoleProtocolVersion.parseVersion("VERSION_012_102_398");
+ Assert.assertNotNull(version);
+ Assert.assertEquals(12, version.getMajor());
+ Assert.assertEquals(102, version.getMinor());
+ Assert.assertEquals(398, version.getPatch());
+ }
+
+ /**
+ * Verifies that invalid version strings fail to parse.
+ */
+ @Test
+ public void testInvalidVersionParse() {
+
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("potato"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION___"));
+
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION__2_3"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1__3"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_2_"));
+
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_A_2_3"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_B_3"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("VERSION_1_2_C"));
+
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("_1_2_3"));
+ Assert.assertNull(GuacamoleProtocolVersion.parseVersion("version_1_2_3"));
+
+ }
+
+ /**
+ * Verifies that the atLeast() function defined by GuacamoleProtocolVersion
+ * behaves as required for a series of three versions which are in strictly
+ * increasing order (a < b < c).
+ *
+ * @param a
+ * The String representation of the version which is known to be the
+ * smaller than versions b and c.
+ *
+ * @param b
+ * The String representation of the version which is known to be
+ * larger than version a but smaller than version c.
+ *
+ * @param c
+ * The String representation of the version which is known to be the
+ * larger than versions a and b.
+ */
+ private void testVersionCompare(String a, String b, String c) {
+
+ GuacamoleProtocolVersion verA = GuacamoleProtocolVersion.parseVersion(a);
+ GuacamoleProtocolVersion verB = GuacamoleProtocolVersion.parseVersion(b);
+ GuacamoleProtocolVersion verC = GuacamoleProtocolVersion.parseVersion(c);
+
+ Assert.assertTrue(verC.atLeast(verB));
+ Assert.assertTrue(verC.atLeast(verA));
+ Assert.assertTrue(verB.atLeast(verA));
+
+ Assert.assertFalse(verB.atLeast(verC));
+ Assert.assertFalse(verA.atLeast(verC));
+ Assert.assertFalse(verA.atLeast(verB));
+
+ Assert.assertTrue(verA.atLeast(verA));
+ Assert.assertTrue(verB.atLeast(verB));
+ Assert.assertTrue(verC.atLeast(verC));
+
+ }
+
+ /**
+ * Verifies that version order comparisons using atLeast() behave as
+ * required.
+ */
+ @Test
+ public void testVersionCompare() {
+ testVersionCompare("VERSION_0_0_1", "VERSION_0_0_2", "VERSION_0_0_3");
+ testVersionCompare("VERSION_0_1_0", "VERSION_0_2_0", "VERSION_0_3_0");
+ testVersionCompare("VERSION_1_0_0", "VERSION_2_0_0", "VERSION_3_0_0");
+ testVersionCompare("VERSION_1_2_3", "VERSION_1_3_3", "VERSION_2_0_0");
+ }
+
+ /**
+ * Verifies that versions can be tested for equality using equals().
+ */
+ @Test
+ public void testVersionEquals() {
+
+ GuacamoleProtocolVersion version;
+
+ version = GuacamoleProtocolVersion.parseVersion("VERSION_012_102_398");
+ Assert.assertTrue(version.equals(version));
+ Assert.assertTrue(version.equals(new GuacamoleProtocolVersion(12, 102, 398)));
+ Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(12, 102, 399)));
+ Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(12, 103, 398)));
+ Assert.assertFalse(version.equals(new GuacamoleProtocolVersion(11, 102, 398)));
+
+ version = GuacamoleProtocolVersion.parseVersion("VERSION_1_0_0");
+ Assert.assertTrue(version.equals(GuacamoleProtocolVersion.VERSION_1_0_0));
+ Assert.assertFalse(version.equals(GuacamoleProtocolVersion.VERSION_1_1_0));
+
+ version = GuacamoleProtocolVersion.parseVersion("VERSION_1_1_0");
+ Assert.assertTrue(version.equals(GuacamoleProtocolVersion.VERSION_1_1_0));
+ Assert.assertFalse(version.equals(GuacamoleProtocolVersion.VERSION_1_0_0));
+
+ }
+
+ /**
+ * Verifies that versions can be converted to their Guacamole protocol
+ * representation through calling toString().
+ */
+ @Test
+ public void testToString() {
+ Assert.assertEquals("VERSION_1_0_0", GuacamoleProtocolVersion.VERSION_1_0_0.toString());
+ Assert.assertEquals("VERSION_1_1_0", GuacamoleProtocolVersion.VERSION_1_1_0.toString());
+ Assert.assertEquals("VERSION_12_103_398", new GuacamoleProtocolVersion(12, 103, 398).toString());
+ }
+
+}
diff --git a/guacamole-docker/bin/start.sh b/guacamole-docker/bin/start.sh
index 6d5af95..be97901 100755
--- a/guacamole-docker/bin/start.sh
+++ b/guacamole-docker/bin/start.sh
@@ -575,6 +575,7 @@
start_guacamole() {
# Install webapp
+ rm -Rf /usr/local/tomcat/webapps/${WEBAPP_CONTEXT:-guacamole}
ln -sf /opt/guacamole/guacamole.war /usr/local/tomcat/webapps/${WEBAPP_CONTEXT:-guacamole}.war
# Start tomcat
diff --git a/guacamole-ext/pom.xml b/guacamole-ext/pom.xml
index e123b9f..a4e7b07 100644
--- a/guacamole-ext/pom.xml
+++ b/guacamole-ext/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<packaging>jar</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-ext</name>
<url>http://guacamole.apache.org/</url>
@@ -155,7 +155,7 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<scope>compile</scope>
</dependency>
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
index 19f1ead..9fe76a4 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/Field.java
@@ -92,6 +92,14 @@
public static String TIMEZONE = "TIMEZONE";
/**
+ * Field type which allows selection of languages. The languages
+ * displayed are the set of languages supported by the Guacamole web
+ * application. Legal values are valid language IDs, as dictated by
+ * the filenames of Guacamole's available translations.
+ */
+ public static String LANGUAGE = "LANGUAGE";
+
+ /**
* A date field whose legal values conform to the pattern "YYYY-MM-DD",
* zero-padded.
*/
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/LanguageField.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/LanguageField.java
new file mode 100644
index 0000000..a87d772
--- /dev/null
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/LanguageField.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.form;
+
+/**
+ * Represents a language field. The field may contain only valid language
+ * identifiers as used by the Guacamole web application for its translations.
+ * Language identifiers are defined by the filenames of the JSON files
+ * containing the translation.
+ */
+public class LanguageField extends Field {
+
+ /**
+ * Creates a new LanguageField with the given name.
+ *
+ * @param name
+ * The unique name to associate with this field.
+ */
+ public LanguageField(String name) {
+ super(name, Field.Type.LANGUAGE);
+ }
+
+ /**
+ * Parses the given string into a language ID string. As any string may be
+ * a valid language ID as long as it has a corresponding translation, the
+ * only transformation currently performed by this function is to ensure
+ * that a blank language string is parsed into null.
+ *
+ * @param language
+ * The language string to parse, which may be null.
+ *
+ * @return
+ * The ID of the language corresponding to the given string, or null if
+ * if the given language string was null or blank.
+ */
+ public static String parse(String language) {
+
+ // Return null if no language is provided
+ if (language == null || language.isEmpty())
+ return null;
+
+ // Otherwise, assume language is already a valid language ID
+ return language;
+
+ }
+
+}
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/form/TimeZoneField.java b/guacamole-ext/src/main/java/org/apache/guacamole/form/TimeZoneField.java
index 9305410..90084e4 100644
--- a/guacamole-ext/src/main/java/org/apache/guacamole/form/TimeZoneField.java
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/form/TimeZoneField.java
@@ -20,8 +20,9 @@
package org.apache.guacamole.form;
/**
- * Represents a time zone field. The field may contain only valid time zone IDs,
- * as dictated by TimeZone.getAvailableIDs().
+ * Represents a time zone field. The field may contain only valid time zone
+ * identifiers, as defined by the IANA time zone database. Such identifiers are
+ * also valid Java time zone IDs as dictated by TimeZone.getAvailableIDs().
*/
public class TimeZoneField extends Field {
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json
index dde853d..3c9d4fe 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/rdp.json
@@ -103,10 +103,15 @@
"ja-jp-qwerty",
"pt-br-qwerty",
"sv-se-qwerty",
+ "da-dk-qwerty",
"tr-tr-qwerty"
]
},
{
+ "name" : "timezone",
+ "type" : "TIMEZONE"
+ },
+ {
"name" : "console",
"type" : "BOOLEAN",
"options" : [ "true" ]
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
index a71e1fb..a16648f 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/ssh.json
@@ -75,6 +75,14 @@
"type" : "TEXT"
},
{
+ "name" : "locale",
+ "type" : "TEXT"
+ },
+ {
+ "name" : "timezone",
+ "type" : "TIMEZONE"
+ },
+ {
"name" : "server-alive-interval",
"type" : "NUMERIC"
}
@@ -89,7 +97,7 @@
"options" : [ "", "127", "8" ]
},
{
- "name" : "terminal-type",
+ "name" : "terminal-type",
"type" : "ENUM",
"options" : [ "", "xterm", "xterm-256color", "vt220", "vt100", "ansi", "linux" ]
}
diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index e1a2b47..7a68606 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole</artifactId>
<packaging>war</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole</name>
<url>http://guacamole.apache.org/</url>
@@ -264,21 +264,21 @@
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
</dependency>
<!-- Guacamole JavaScript API -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>
@@ -493,6 +493,13 @@
</exclusions>
</dependency>
+
+ <!-- JSTZ for TimeZone Detection -->
+ <dependency>
+ <groupId>org.webjars.npm</groupId>
+ <artifactId>jstz</artifactId>
+ <version>1.0.10</version>
+ </dependency>
</dependencies>
diff --git a/guacamole/src/licenses/LICENSE b/guacamole/src/licenses/LICENSE
index 51f5b21..1e228d2 100644
--- a/guacamole/src/licenses/LICENSE
+++ b/guacamole/src/licenses/LICENSE
@@ -605,6 +605,36 @@
terms above.
+JSTZ (https://pellepim.bitbucket.io/jstz/)
+------------------------------------------
+
+ Version: 1.0.10
+ From: 'Jon Nylander' (https://pellepim.bitbucket.io/jstz/)
+ License(s):
+ MIT (bundled/jstz-1.0.10/LICENSE)
+
+Copyright (c) 2012 Jon Nylander, project maintained at
+https://bitbucket.org/pellepim/jstimezonedetect
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
Logback (http://logback.qos.ch/)
--------------------------------
diff --git a/guacamole/src/licenses/bundled/jstz-1.0.10/LICENSE b/guacamole/src/licenses/bundled/jstz-1.0.10/LICENSE
new file mode 100644
index 0000000..c48af16
--- /dev/null
+++ b/guacamole/src/licenses/bundled/jstz-1.0.10/LICENSE
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2012 Jon Nylander, project maintained at
+https://bitbucket.org/pellepim/jstimezonedetect
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
index ae8c463..0a424ab 100644
--- a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
+++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionModule.java
@@ -62,7 +62,8 @@
private static final List<String> ALLOWED_GUACAMOLE_VERSIONS =
Collections.unmodifiableList(Arrays.asList(
"*",
- "1.0.0"
+ "1.0.0",
+ "1.1.0"
));
/**
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java
index 8c23dab..74e3b4d 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java
@@ -95,6 +95,11 @@
* once for each mimetype.
*/
public static final String IMAGE_PARAMETER = "GUAC_IMAGE";
+
+ /**
+ * The name of the parameter specifying the timezone of the client.
+ */
+ public static final String TIMEZONE_PARAMETER = "GUAC_TIMEZONE";
/**
* All supported object types that can be used as the destination of a
@@ -365,5 +370,16 @@
public List<String> getImageMimetypes() {
return getParameterValues(IMAGE_PARAMETER);
}
-
+
+ /**
+ * Returns the tz database value of the timezone declared by the client
+ * within the tunnel request.
+ *
+ * @return
+ * The tz database value of the timezone parameter as reported by
+ * the client.
+ */
+ public String getTimezone() {
+ return getParameter(TIMEZONE_PARAMETER);
+ }
}
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
index 1479d82..598a4e5 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java
@@ -166,6 +166,11 @@
List<String> imageMimetypes = request.getImageMimetypes();
if (imageMimetypes != null)
info.getImageMimetypes().addAll(imageMimetypes);
+
+ // Set timezone if provided
+ String timezone = request.getTimezone();
+ if (timezone != null && !timezone.isEmpty())
+ info.setTimezone(timezone);
return info;
}
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index a9bc3be..b4637e9 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -42,6 +42,7 @@
var authenticationService = $injector.get('authenticationService');
var connectionGroupService = $injector.get('connectionGroupService');
var connectionService = $injector.get('connectionService');
+ var preferenceService = $injector.get('preferenceService');
var requestService = $injector.get('requestService');
var tunnelService = $injector.get('tunnelService');
var guacAudio = $injector.get('guacAudio');
@@ -225,6 +226,7 @@
+ "&GUAC_WIDTH=" + Math.floor(optimal_width)
+ "&GUAC_HEIGHT=" + Math.floor(optimal_height)
+ "&GUAC_DPI=" + Math.floor(optimal_dpi)
+ + "&GUAC_TIMEZONE=" + encodeURIComponent(preferenceService.preferences.timezone)
+ (connectionParameters ? '&' + connectionParameters : '');
// Add audio mimetypes to connect string
diff --git a/guacamole/src/main/webapp/app/form/controllers/languageFieldController.js b/guacamole/src/main/webapp/app/form/controllers/languageFieldController.js
new file mode 100644
index 0000000..fdab137
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/controllers/languageFieldController.js
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/**
+ * Controller for the language field type. The language field type allows the
+ * user to select a language from the set of languages supported by the
+ * Guacamole web application.
+ */
+angular.module('form').controller('languageFieldController', ['$scope', '$injector',
+ function languageFieldController($scope, $injector) {
+
+ // Required services
+ var languageService = $injector.get('languageService');
+ var requestService = $injector.get('requestService');
+
+ /**
+ * A map of all available language keys to their human-readable
+ * names.
+ *
+ * @type Object.<String, String>
+ */
+ $scope.languages = null;
+
+ // Retrieve defined languages
+ languageService.getLanguages().then(function languagesRetrieved(languages) {
+ $scope.$apply(function updateLanguageOptions() {
+ $scope.languages = languages;
+ });
+ }, requestService.DIE);
+
+ // Interpret undefined/null as empty string
+ $scope.$watch('model', function setModel(model) {
+ if (!model && model !== '')
+ $scope.model = '';
+ });
+
+}]);
diff --git a/guacamole/src/main/webapp/app/form/controllers/timeZoneFieldController.js b/guacamole/src/main/webapp/app/form/controllers/timeZoneFieldController.js
index 5f17915..39f0c38 100644
--- a/guacamole/src/main/webapp/app/form/controllers/timeZoneFieldController.js
+++ b/guacamole/src/main/webapp/app/form/controllers/timeZoneFieldController.js
@@ -19,8 +19,9 @@
/**
- * Controller for time zone fields. Time zone fields use Java IDs as the
- * standard representation for each supported time zone.
+ * Controller for time zone fields. Time zone fields use IANA time zone
+ * database identifiers as the standard representation for each supported time
+ * zone. These identifiers are also legal Java time zone IDs.
*/
angular.module('form').controller('timeZoneFieldController', ['$scope', '$injector',
function timeZoneFieldController($scope, $injector) {
@@ -418,7 +419,6 @@
"Canada" : {
"Atlantic" : "Canada/Atlantic",
"Central" : "Canada/Central",
- "East-Saskatchewan" : "Canada/East-Saskatchewan",
"Eastern" : "Canada/Eastern",
"Mountain" : "Canada/Mountain",
"Newfoundland" : "Canada/Newfoundland",
diff --git a/guacamole/src/main/webapp/app/form/formModule.js b/guacamole/src/main/webapp/app/form/formModule.js
index 7e6ede9..1135118 100644
--- a/guacamole/src/main/webapp/app/form/formModule.js
+++ b/guacamole/src/main/webapp/app/form/formModule.js
@@ -20,4 +20,7 @@
/**
* Module for displaying dynamic forms.
*/
-angular.module('form', ['locale']);
+angular.module('form', [
+ 'locale',
+ 'rest'
+]);
diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js
index 168a1ef..6019e74 100644
--- a/guacamole/src/main/webapp/app/form/services/formService.js
+++ b/guacamole/src/main/webapp/app/form/services/formService.js
@@ -131,6 +131,21 @@
},
/**
+ * Field type which allows selection of languages. The languages
+ * displayed are the set of languages supported by the Guacamole web
+ * application. Legal values are valid language IDs, as dictated by
+ * the filenames of Guacamole's available translations.
+ *
+ * @see {@link Field.Type.LANGUAGE}
+ * @type FieldType
+ */
+ 'LANGUAGE' : {
+ module : 'form',
+ controller : 'languageFieldController',
+ templateUrl : 'app/form/templates/languageField.html'
+ },
+
+ /**
* Field type which allows selection of time zones.
*
* @see {@link Field.Type.TIMEZONE}
diff --git a/guacamole/src/main/webapp/app/form/templates/languageField.html b/guacamole/src/main/webapp/app/form/templates/languageField.html
new file mode 100644
index 0000000..404f74e
--- /dev/null
+++ b/guacamole/src/main/webapp/app/form/templates/languageField.html
@@ -0,0 +1 @@
+<select ng-model="model" ng-options="language.key as language.value for language in languages | toArray | orderBy: key"></select>
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
index 71e7af7..aad0a2e 100644
--- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
+++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
@@ -39,7 +39,6 @@
var $translate = $injector.get('$translate');
var authenticationService = $injector.get('authenticationService');
var guacNotification = $injector.get('guacNotification');
- var languageService = $injector.get('languageService');
var permissionService = $injector.get('permissionService');
var preferenceService = $injector.get('preferenceService');
var requestService = $injector.get('requestService');
@@ -78,21 +77,23 @@
* @type Object.<String, Object>
*/
$scope.preferences = preferenceService.preferences;
-
+
/**
- * A map of all available language keys to their human-readable
- * names.
- *
- * @type Object.<String, String>
+ * The fields which should be displayed for choosing locale
+ * preferences. Each field name must be a property on
+ * $scope.preferences.
+ *
+ * @type Field[]
*/
- $scope.languages = null;
-
- /**
- * Switches the active display langugae to the chosen language.
- */
- $scope.changeLanguage = function changeLanguage() {
- $translate.use($scope.preferences.language);
- };
+ $scope.localeFields = [
+ { 'type' : 'LANGUAGE', 'name' : 'language' },
+ { 'type' : 'TIMEZONE', 'name' : 'timezone' }
+ ];
+
+ // Automatically update applied translation when language preference is changed
+ $scope.$watch('preferences.language', function changeLanguage(language) {
+ $translate.use(language);
+ });
/**
* The new password for the user.
@@ -169,17 +170,6 @@
};
- // Retrieve defined languages
- languageService.getLanguages()
- .then(function languagesRetrieved(languages) {
- $scope.languages = Object.keys(languages).map(function(key) {
- return {
- key: key,
- value: languages[key]
- };
- });
- }, requestService.DIE);
-
// Retrieve current permissions
permissionService.getEffectivePermissions(dataSource, username)
.then(function permissionsRetrieved(permissions) {
diff --git a/guacamole/src/main/webapp/app/settings/services/preferenceService.js b/guacamole/src/main/webapp/app/settings/services/preferenceService.js
index bcd8633..9c4d5fc 100644
--- a/guacamole/src/main/webapp/app/settings/services/preferenceService.js
+++ b/guacamole/src/main/webapp/app/settings/services/preferenceService.js
@@ -98,6 +98,18 @@
return language.replace(/-/g, '_');
};
+
+ /**
+ * Return the timezone detected for the current browser session
+ * by the JSTZ timezone library.
+ *
+ * @returns String
+ * The name of the currently-detected timezone in IANA zone key
+ * format (Olson time zone database).
+ */
+ var getDetectedTimezone = function getDetectedTimezone() {
+ return jstz.determine().name();
+ };
/**
* All currently-set preferences, as name/value pairs. Each property name
@@ -128,7 +140,15 @@
*
* @type String
*/
- language : getDefaultLanguageKey()
+ language : getDefaultLanguageKey(),
+
+ /**
+ * The timezone set by the user, in IANA zone key format (Olson time
+ * zone database).
+ *
+ * @type String
+ */
+ timezone : getDetectedTimezone()
};
diff --git a/guacamole/src/main/webapp/app/settings/styles/preferences.css b/guacamole/src/main/webapp/app/settings/styles/preferences.css
index ed8460d..dbb2330 100644
--- a/guacamole/src/main/webapp/app/settings/styles/preferences.css
+++ b/guacamole/src/main/webapp/app/settings/styles/preferences.css
@@ -17,8 +17,23 @@
* under the License.
*/
-.preferences .update-password .form,
-.preferences .language .form {
+.preferences .form .fields {
+ display: table;
padding-left: 0.5em;
- border-left: 3px solid rgba(0, 0, 0, 0.125);
-}
\ No newline at end of file
+ border-left: 3px solid rgba(0,0,0,0.125);
+}
+
+.preferences .form .fields .labeled-field {
+ display: table-row;
+}
+
+.preferences .form .fields .field-header,
+.preferences .form .fields .form-field {
+ display: table-cell;
+ padding: 0.125em;
+ vertical-align: top;
+}
+
+.preferences .form .fields .field-header {
+ padding-right: 1em;
+}
diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html
index 826a5cd..581a66e 100644
--- a/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html
+++ b/guacamole/src/main/webapp/app/settings/templates/settingsPreferences.html
@@ -1,18 +1,9 @@
<div class="preferences" ng-class="{loading: !isLoaded()}">
- <!-- Language settings -->
- <div class="settings section language">
- <p>{{'SETTINGS_PREFERENCES.HELP_LANGUAGE' | translate}}</p>
-
- <!-- Language selection -->
- <div class="form">
- <table class="fields">
- <tr>
- <th>{{'SETTINGS_PREFERENCES.FIELD_HEADER_LANGUAGE' | translate}}</th>
- <td><select ng-model="preferences.language" ng-change="changeLanguage()" ng-options="language.key as language.value for language in languages | orderBy: key"></select></td>
- </tr>
- </table>
- </div>
+ <!-- Locale settings -->
+ <div class="settings section locale">
+ <p>{{'SETTINGS_PREFERENCES.HELP_LOCALE' | translate}}</p>
+ <guac-form content="localeFields" model="preferences" namespace="'SETTINGS_PREFERENCES'"></guac-form>
</div>
<!-- Password update -->
diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html
index 1d51606..e675546 100644
--- a/guacamole/src/main/webapp/index.html
+++ b/guacamole/src/main/webapp/index.html
@@ -85,6 +85,9 @@
<script type="text/javascript" src="webjars/angular-translate-interpolation-messageformat/2.16.0/angular-translate-interpolation-messageformat.min.js"></script>
<script type="text/javascript" src="webjars/angular-translate-loader-static-files/2.16.0/angular-translate-loader-static-files.min.js"></script>
+ <!-- JSTZ -->
+ <script type="text/javascript" src="webjars/jstz/1.0.10/dist/jstz.min.js"></script>
+
<!-- Polyfills for the "datalist" element, Blob and the FileSaver API -->
<script type="text/javascript" src="webjars/blob-polyfill/1.0.20150320/Blob.js"></script>
<script type="text/javascript" src="webjars/datalist-polyfill/1.14.0/datalist-polyfill.min.js"></script>
diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json
index eed9473..2123de0 100644
--- a/guacamole/src/main/webapp/translations/en.json
+++ b/guacamole/src/main/webapp/translations/en.json
@@ -428,6 +428,7 @@
"FIELD_HEADER_SFTP_ROOT_DIRECTORY" : "File browser root directory:",
"FIELD_HEADER_SFTP_USERNAME" : "Username:",
"FIELD_HEADER_STATIC_CHANNELS" : "Static channel names:",
+ "FIELD_HEADER_TIMEZONE" : "Time zone:",
"FIELD_HEADER_USERNAME" : "Username:",
"FIELD_HEADER_WIDTH" : "Width:",
@@ -460,6 +461,7 @@
"FIELD_OPTION_SERVER_LAYOUT_JA_JP_QWERTY" : "Japanese (Qwerty)",
"FIELD_OPTION_SERVER_LAYOUT_PT_BR_QWERTY" : "Portuguese Brazilian (Qwerty)",
"FIELD_OPTION_SERVER_LAYOUT_SV_SE_QWERTY" : "Swedish (Qwerty)",
+ "FIELD_OPTION_SERVER_LAYOUT_DA_DK_QWERTY" : "Danish (Qwerty)",
"FIELD_OPTION_SERVER_LAYOUT_TR_TR_QWERTY" : "Turkish-Q (Qwerty)",
"NAME" : "RDP",
@@ -491,6 +493,7 @@
"FIELD_HEADER_ENABLE_SFTP" : "Enable SFTP:",
"FIELD_HEADER_HOST_KEY" : "Public host key (Base64):",
"FIELD_HEADER_HOSTNAME" : "Hostname:",
+ "FIELD_HEADER_LOCALE" : "Language/Locale ($LANG):",
"FIELD_HEADER_USERNAME" : "Username:",
"FIELD_HEADER_PASSWORD" : "Password:",
"FIELD_HEADER_PASSPHRASE" : "Passphrase:",
@@ -505,6 +508,7 @@
"FIELD_HEADER_SERVER_ALIVE_INTERVAL" : "Server keepalive interval:",
"FIELD_HEADER_SFTP_ROOT_DIRECTORY" : "File browser root directory:",
"FIELD_HEADER_TERMINAL_TYPE" : "Terminal type:",
+ "FIELD_HEADER_TIMEZONE" : "Time zone ($TZ):",
"FIELD_HEADER_TYPESCRIPT_NAME" : "Typescript name:",
"FIELD_HEADER_TYPESCRIPT_PATH" : "Typescript path:",
@@ -754,6 +758,7 @@
"FIELD_HEADER_PASSWORD_OLD" : "Current Password:",
"FIELD_HEADER_PASSWORD_NEW" : "New Password:",
"FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Confirm New Password:",
+ "FIELD_HEADER_TIMEZONE" : "Timezone:",
"FIELD_HEADER_USERNAME" : "Username:",
"HELP_DEFAULT_INPUT_METHOD" : "The default input method determines how keyboard events are received by Guacamole. Changing this setting may be necessary when using a mobile device, or when typing through an IME. This setting can be overridden on a per-connection basis within the Guacamole menu.",
@@ -761,7 +766,7 @@
"HELP_INPUT_METHOD_NONE" : "@:CLIENT.HELP_INPUT_METHOD_NONE",
"HELP_INPUT_METHOD_OSK" : "@:CLIENT.HELP_INPUT_METHOD_OSK",
"HELP_INPUT_METHOD_TEXT" : "@:CLIENT.HELP_INPUT_METHOD_TEXT",
- "HELP_LANGUAGE" : "Select a different language below to change the language of all text within Guacamole. Available choices will depend on which languages are installed.",
+ "HELP_LOCALE" : "Options below are related to the locale of the user and will impact how various parts of the interface are displayed.",
"HELP_MOUSE_MODE_ABSOLUTE" : "@:CLIENT.HELP_MOUSE_MODE_ABSOLUTE",
"HELP_MOUSE_MODE_RELATIVE" : "@:CLIENT.HELP_MOUSE_MODE_RELATIVE",
"HELP_UPDATE_PASSWORD" : "If you wish to change your password, enter your current password and the desired new password below, and click \"Update Password\". The change will take effect immediately.",
diff --git a/guacamole/src/main/webapp/translations/zh.json b/guacamole/src/main/webapp/translations/zh.json
index eca8656..b940418 100644
--- a/guacamole/src/main/webapp/translations/zh.json
+++ b/guacamole/src/main/webapp/translations/zh.json
@@ -4,9 +4,6 @@
"APP" : {
- "NAME" : "Apache Guacamole",
- "VERSION" : "${project.version}",
-
"ACTION_ACKNOWLEDGE" : "确定",
"ACTION_CANCEL" : "取消",
"ACTION_CLONE" : "克隆",
diff --git a/pom.xml b/pom.xml
index 480eb23..83cfa8e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-client</artifactId>
<packaging>pom</packaging>
- <version>1.0.0</version>
+ <version>1.1.0</version>
<name>guacamole-client</name>
<url>http://guacamole.apache.org/</url>