Merge 1.1.0 changes back to master.
diff --git a/extensions/guacamole-auth-cas/pom.xml b/extensions/guacamole-auth-cas/pom.xml
index 69ecab5..3afabda 100644
--- a/extensions/guacamole-auth-cas/pom.xml
+++ b/extensions/guacamole-auth-cas/pom.xml
@@ -234,6 +234,13 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        
+        <!-- Guava - Utility Library -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.0.1-jre</version>
+        </dependency>
 
         <!-- Guice -->
         <dependency>
@@ -254,6 +261,14 @@
             <version>2.5</version>
             <scope>provided</scope>
         </dependency>
+        
+        <!-- Jersey - JAX-RS Implementation -->
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/extensions/guacamole-auth-cas/src/licenses/LICENSE b/extensions/guacamole-auth-cas/src/licenses/LICENSE
index 1be0f68..6edeee8 100644
--- a/extensions/guacamole-auth-cas/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-cas/src/licenses/LICENSE
@@ -219,6 +219,7 @@
     License(s):
         Public Domain (bundled/aopalliance-1.0/LICENSE)
 
+
 Google Guice (https://github.com/google/guice)
 ----------------------------------------------
 
@@ -227,6 +228,16 @@
     License(s):
         Apache v2.0 (bundled/guice-3.0/COPYING)
 
+
+Guava: Google Core Libraries for Java (https://github.com/google/guava)
+-----------------------------------------------------------------------
+
+    Version: 27.0.1-jre
+    From: 'Google Inc.' (http://www.google.com/)
+    License(s):
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
+
+
 JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
 ----------------------------------------------------------------------------
 
@@ -234,3 +245,4 @@
     From: 'JSR-330 Expert Group' (https://jcp.org/en/jsr/detail?id=330)
     License(s):
         Apache v2.0 (bundled/javax.inject-1/LICENSE-2.0.txt)
+
diff --git a/extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING b/extensions/guacamole-auth-cas/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
copy from extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING
copy to extensions/guacamole-auth-cas/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java
index dd741a3..2ee42db 100644
--- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java
+++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java
@@ -19,7 +19,7 @@
 
 package org.apache.guacamole.auth.cas.conf;
 
-import org.apache.guacamole.properties.StringGuacamoleProperty;
+import org.apache.guacamole.properties.URIGuacamoleProperty;
 
 /**
  * Provides properties required for use of the CAS authentication provider.
@@ -36,8 +36,8 @@
     /**
      * The authorization endpoint (URI) of the CAS service.
      */
-    public static final StringGuacamoleProperty CAS_AUTHORIZATION_ENDPOINT =
-            new StringGuacamoleProperty() {
+    public static final URIGuacamoleProperty CAS_AUTHORIZATION_ENDPOINT =
+            new URIGuacamoleProperty() {
 
         @Override
         public String getName() { return "cas-authorization-endpoint"; }
@@ -49,8 +49,8 @@
      * authentication process is complete. This must be the full URL that a
      * user would enter into their browser to access Guacamole.
      */
-    public static final StringGuacamoleProperty CAS_REDIRECT_URI =
-            new StringGuacamoleProperty() {
+    public static final URIGuacamoleProperty CAS_REDIRECT_URI =
+            new URIGuacamoleProperty() {
 
         @Override
         public String getName() { return "cas-redirect-uri"; }
diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java
index e0016ad..680f170 100644
--- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java
+++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java
@@ -20,6 +20,7 @@
 package org.apache.guacamole.auth.cas.conf;
 
 import com.google.inject.Inject;
+import java.net.URI;
 import java.security.PrivateKey;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.environment.Environment;
@@ -47,7 +48,7 @@
      *     If guacamole.properties cannot be parsed, or if the authorization
      *     endpoint property is missing.
      */
-    public String getAuthorizationEndpoint() throws GuacamoleException {
+    public URI getAuthorizationEndpoint() throws GuacamoleException {
         return environment.getRequiredProperty(CASGuacamoleProperties.CAS_AUTHORIZATION_ENDPOINT);
     }
 
@@ -65,7 +66,7 @@
      *     If guacamole.properties cannot be parsed, or if the redirect URI
      *     property is missing.
      */
-    public String getRedirectURI() throws GuacamoleException {
+    public URI getRedirectURI() throws GuacamoleException {
         return environment.getRequiredProperty(CASGuacamoleProperties.CAS_REDIRECT_URI);
     }
 
diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
index f785241..c16f525 100644
--- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
+++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/form/CASTicketField.java
@@ -19,8 +19,8 @@
 
 package org.apache.guacamole.auth.cas.form;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
+import java.net.URI;
+import javax.ws.rs.core.UriBuilder;
 import org.apache.guacamole.form.Field;
 
 
@@ -47,7 +47,7 @@
     /**
      * The full URI which the field should link to.
      */
-    private final String authorizationURI;
+    private final URI authorizationURI;
 
     /**
      * Creates a new CAS "ticket" field which links to the given CAS
@@ -65,29 +65,15 @@
      *     The URI that the CAS service should redirect to upon successful
      *     authentication.
      */
-    public CASTicketField(String authorizationEndpoint, String redirectURI) {
+    public CASTicketField(URI authorizationEndpoint, URI redirectURI) {
 
         // Init base field properties
         super(PARAMETER_NAME, "GUAC_CAS_TICKET");
-
-        // Build authorization URI from given values
-        try {
-            final StringBuilder sb = new StringBuilder();
-            sb.append(authorizationEndpoint);
-            // user might configure the endpoint with a trailing slash
-            if (sb.charAt(sb.length() - 1) != '/') {
-                sb.append('/');
-            }
-            sb.append(CAS_LOGIN_URI);
-            sb.append("?service=");
-            sb.append(URLEncoder.encode(redirectURI, "UTF-8"));
-            this.authorizationURI = sb.toString();
-        }
-
-        // Java is required to provide UTF-8 support
-        catch (UnsupportedEncodingException e) {
-            throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e);
-        }
+        
+        this.authorizationURI = UriBuilder.fromUri(authorizationEndpoint)
+                .path(CAS_LOGIN_URI)
+                .queryParam("service", redirectURI)
+                .build();
 
     }
 
@@ -99,7 +85,7 @@
      *     The full URI that this field should link to.
      */
     public String getAuthorizationURI() {
-        return authorizationURI;
+        return authorizationURI.toString();
     }
 
 }
diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java
index ba7ac83..fce4760 100644
--- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java
+++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java
@@ -19,7 +19,9 @@
 
 package org.apache.guacamole.auth.cas.ticket;
 
+import com.google.common.io.BaseEncoding;
 import com.google.inject.Inject;
+import java.net.URI;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
@@ -31,7 +33,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleSecurityException;
 import org.apache.guacamole.GuacamoleServerException;
@@ -93,14 +94,14 @@
         // Retrieve the configured CAS URL, establish a ticket validator,
         // and then attempt to validate the supplied ticket.  If that succeeds,
         // grab the principal returned by the validator.
-        String casServerUrl = confService.getAuthorizationEndpoint();
-        Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrl);
+        URI casServerUrl = confService.getAuthorizationEndpoint();
+        Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrl.toString());
         validator.setAcceptAnyProxy(true);
         validator.setEncoding("UTF-8");
         try {
             Map<String, String> tokens = new HashMap<>();
-            String confRedirectURI = confService.getRedirectURI();
-            Assertion a = validator.validate(ticket, confRedirectURI);
+            URI confRedirectURI = confService.getRedirectURI();
+            Assertion a = validator.validate(ticket, confRedirectURI.toString());
             AttributePrincipal principal =  a.getPrincipal();
             Map<String, Object> ticketAttrs =
                     new HashMap<>(principal.getAttributes());
@@ -181,7 +182,7 @@
             cipher.init(Cipher.DECRYPT_MODE, clearpassKey);
 
             // Decode and decrypt, and return a new string.
-            final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword);
+            final byte[] pass64 = BaseEncoding.base64().decode(encryptedPassword);
             final byte[] cipherData = cipher.doFinal(pass64);
             return new String(cipherData, Charset.forName("UTF-8"));
 
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 d037fbb..40d0334 100644
--- a/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
@@ -10,7 +10,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "js" : [
diff --git a/extensions/guacamole-auth-cas/src/main/resources/translations/ja.json b/extensions/guacamole-auth-cas/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..2afdb76
--- /dev/null
+++ b/extensions/guacamole-auth-cas/src/main/resources/translations/ja.json
@@ -0,0 +1,7 @@
+{
+
+    "LOGIN" : {
+        "INFO_CAS_REDIRECT_PENDING"  : "CAS認証にリダイレクトしています。"
+    }
+
+}
diff --git a/extensions/guacamole-auth-duo/pom.xml b/extensions/guacamole-auth-duo/pom.xml
index 330f76e..8de36e0 100644
--- a/extensions/guacamole-auth-duo/pom.xml
+++ b/extensions/guacamole-auth-duo/pom.xml
@@ -216,6 +216,13 @@
             <version>1.1.0</version>
             <scope>provided</scope>
         </dependency>
+        
+        <!-- Guava - Utility Library -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.0.1-jre</version>
+        </dependency>
 
         <!-- Guice -->
         <dependency>
diff --git a/extensions/guacamole-auth-duo/src/licenses/LICENSE b/extensions/guacamole-auth-duo/src/licenses/LICENSE
index 4ae92c4..b357f34 100644
--- a/extensions/guacamole-auth-duo/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-duo/src/licenses/LICENSE
@@ -264,6 +264,15 @@
         Apache v2.0 (bundled/guice-3.0/COPYING)
 
 
+Guava: Google Core Libraries for Java (https://github.com/google/guava)
+-----------------------------------------------------------------------
+
+    Version: 27.0.1-jre
+    From: 'Google Inc.' (http://www.google.com/)
+    License(s):
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
+
+
 JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
 ----------------------------------------------------------------------------
 
diff --git a/extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING b/extensions/guacamole-auth-duo/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
copy from extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING
copy to extensions/guacamole-auth-duo/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/DuoCookie.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/DuoCookie.java
index 1de9a67..6fa2a88 100644
--- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/DuoCookie.java
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/DuoCookie.java
@@ -19,10 +19,10 @@
 
 package org.apache.guacamole.auth.duo.api;
 
+import com.google.common.io.BaseEncoding;
 import java.io.UnsupportedEncodingException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleClientException;
 import org.apache.guacamole.GuacamoleException;
 
@@ -171,7 +171,7 @@
         // Attempt to decode data as base64
         String data;
         try {
-            data = new String(DatatypeConverter.parseBase64Binary(str), "UTF-8");
+            data = new String(BaseEncoding.base64().decode(str), "UTF-8");
         }
 
         // Bail if invalid base64 is provided
@@ -231,7 +231,7 @@
             String data = username + "|" + integrationKey + "|" + expires;
 
             // Encode resulting cookie string with base64
-            return DatatypeConverter.printBase64Binary(data.getBytes("UTF-8"));
+            return BaseEncoding.base64().encode(data.getBytes("UTF-8"));
 
         }
 
diff --git a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/SignedDuoCookie.java b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/SignedDuoCookie.java
index 49fb34b..c959acd 100644
--- a/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/SignedDuoCookie.java
+++ b/extensions/guacamole-auth-duo/src/main/java/org/apache/guacamole/auth/duo/api/SignedDuoCookie.java
@@ -19,6 +19,7 @@
 
 package org.apache.guacamole.auth.duo.api;
 
+import com.google.common.io.BaseEncoding;
 import java.io.UnsupportedEncodingException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -26,7 +27,6 @@
 import java.util.regex.Pattern;
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleClientException;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleServerException;
@@ -221,7 +221,7 @@
             mac.init(new SecretKeySpec(key.getBytes("UTF-8"), SIGNATURE_ALGORITHM));
 
             // Return signature as hex
-            return DatatypeConverter.printHexBinary(mac.doFinal(data.getBytes("UTF-8"))).toLowerCase();
+            return BaseEncoding.base16().lowerCase().encode(mac.doFinal(data.getBytes("UTF-8")));
 
         }
 
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 6de72fa..7daefdd 100644
--- a/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-duo/src/main/resources/guac-manifest.json
@@ -10,7 +10,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "js" : [
diff --git a/extensions/guacamole-auth-duo/src/main/resources/translations/ja.json b/extensions/guacamole-auth-duo/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..37ddde2
--- /dev/null
+++ b/extensions/guacamole-auth-duo/src/main/resources/translations/ja.json
@@ -0,0 +1,8 @@
+{
+
+    "LOGIN" : {
+        "INFO_DUO_VALIDATION_CODE_INCORRECT"    : "Duoの認証コードが間違っています。",
+        "INFO_DUO_AUTH_REQUIRED"                : "Duoで認証してください。"
+    }
+
+}
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 fdc8003..23c374b 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
@@ -135,7 +135,7 @@
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
-            <version>19.0</version>
+            <version>27.0.1-jre</version>
         </dependency>
 
     </dependencies>
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java
index ebcd1cd..83055d9 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java
@@ -19,10 +19,10 @@
 
 package org.apache.guacamole.auth.jdbc.security;
 
+import com.google.common.io.BaseEncoding;
 import java.io.UnsupportedEncodingException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import javax.xml.bind.DatatypeConverter;
 
 /**
  * Provides a SHA-256 based implementation of the password encryption
@@ -40,7 +40,7 @@
             builder.append(password);
 
             if (salt != null)
-                builder.append(DatatypeConverter.printHexBinary(salt));
+                builder.append(BaseEncoding.base16().encode(salt));
 
             // Hash UTF-8 bytes of possibly-salted password
             MessageDigest md = MessageDigest.getInstance("SHA-256");
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/ja.json b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..de5da9a
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/resources/translations/ja.json
@@ -0,0 +1,96 @@
+{
+
+    "LOGIN" : {
+
+        "ERROR_PASSWORD_BLANK"    : "@:APP.ERROR_PASSWORD_BLANK",
+        "ERROR_PASSWORD_SAME"     : "新しいパスワードには、無効になったパスワードとは別の文字を使用してください。",
+        "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
+        "ERROR_NOT_VALID"         : "このユーザアカウントは、現在有効ではありません。",
+        "ERROR_NOT_ACCESSIBLE"    : "このユーザのアクセスは現在許可されていません。 しばらくしてから再度アクセスをしてください。",
+
+        "INFO_PASSWORD_EXPIRED" : "あなたのパスワードは無効なため、リセットが必要です。 新しいパスワードを入力してください。",
+
+        "FIELD_HEADER_NEW_PASSWORD"         : "新しいパスワード",
+        "FIELD_HEADER_CONFIRM_NEW_PASSWORD" : "新しいパスワード(確認)"
+
+    },
+
+    "CONNECTION_ATTRIBUTES" : {
+
+        "FIELD_HEADER_MAX_CONNECTIONS"          : "最大接続数:",
+        "FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "ユーザ毎の最大接続数:",
+
+        "FIELD_HEADER_FAILOVER_ONLY"            : "フェイルオーバ機能のみを使用:",
+        "FIELD_HEADER_WEIGHT"                   : "コネクションウェイト:",
+
+        "FIELD_HEADER_GUACD_HOSTNAME"   : "ホスト名:",
+        "FIELD_HEADER_GUACD_ENCRYPTION" : "暗号化:",
+        "FIELD_HEADER_GUACD_PORT"       : "ポート:",
+
+        "FIELD_OPTION_GUACD_ENCRYPTION_NONE"  : "なし (暗号化なし)",
+
+        "SECTION_HEADER_CONCURRENCY"    : "同時接続制限",
+        "SECTION_HEADER_LOAD_BALANCING" : "ロードバラシング",
+        "SECTION_HEADER_GUACD"          : "Guacamoleプロキシパラメータ (guacd)"
+
+    },
+
+    "CONNECTION_GROUP_ATTRIBUTES" : {
+
+        "FIELD_HEADER_ENABLE_SESSION_AFFINITY"  : "セッションアフィニティの有効化:",
+        "FIELD_HEADER_MAX_CONNECTIONS"          : "最大接続数:",
+        "FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "ユーザ毎の最大接続数:",
+
+        "SECTION_HEADER_CONCURRENCY" : "同時接続制限(バランシンググループ)"
+
+    },
+
+    "DATA_SOURCE_MYSQL_SHARED" : {
+        "NAME" : "共有接続 (MySQL)"
+    },
+
+    "DATA_SOURCE_POSTGRESQL_SHARED" : {
+        "NAME" : "共有接続 (PostgreSQL)"
+    },
+
+    "DATA_SOURCE_SQLSERVER_SHARED" : {
+        "NAME" : "共有接続 (SQL Server)"
+    },
+
+    "HOME" : {
+        "INFO_SHARED_BY" : "{USERNAME}によって共有されています"
+    },
+
+    "PASSWORD_POLICY" : {
+
+        "ERROR_CONTAINS_USERNAME"      : "ユーザ名にパスワードを含んでいます。",
+        "ERROR_REQUIRES_DIGIT"         : "パスワードには数字を含めてください。",
+        "ERROR_REQUIRES_MULTIPLE_CASE" : "パスワードにはアルファベットの大文字・小文字を含めてください。",
+        "ERROR_REQUIRES_NON_ALNUM"     : "パスワードには記号を含めてください。"
+
+    },
+
+    "USER_ATTRIBUTES" : {
+
+        "FIELD_HEADER_DISABLED"            : "ログインの無効化:",
+        "FIELD_HEADER_EXPIRED"             : "パスワードの期限:",
+        "FIELD_HEADER_ACCESS_WINDOW_END"   : "指定した時刻からアクセスを禁止する:",
+        "FIELD_HEADER_ACCESS_WINDOW_START" : "指定した時刻からアクセスを許可する:",
+        "FIELD_HEADER_TIMEZONE"            : "ユーザのタイムゾーン:",
+        "FIELD_HEADER_VALID_FROM"          : "指定した日からアカウントを有効化する:",
+        "FIELD_HEADER_VALID_UNTIL"         : "指定した日からアカウントを無効化する:",
+
+        "SECTION_HEADER_RESTRICTIONS" : "アカウント制限",
+        "SECTION_HEADER_PROFILE"      : "プロフィール"
+
+    },
+
+    "USER_GROUP_ATTRIBUTES" : {
+
+        "FIELD_HEADER_DISABLED" : "グループの無効化:",
+
+        "SECTION_HEADER_RESTRICTIONS" : "グループ制限"
+
+    }
+
+}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/LICENSE b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/LICENSE
index 4bf8148..7d27dd1 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/LICENSE
@@ -232,10 +232,10 @@
 Guava: Google Core Libraries for Java (https://github.com/google/guava)
 -----------------------------------------------------------------------
 
-    Version: 18.0
+    Version: 27.0.1-jre
     From: 'Google Inc.' (http://www.google.com/)
     License(s):
-        Apache v2.0 (bundled/guava-18.0/COPYING)
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
 
 
 JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
@@ -263,4 +263,3 @@
     From: 'MyBatis' (http://www.mybatis.org/)
     License(s):
         Apache v2.0 (bundled/mybatis-guice-3.6/LICENSE)
-
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-18.0/COPYING b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-18.0/COPYING
rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/jaxb-api-2.3.1/license.txt b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/jaxb-api-2.3.1/license.txt
new file mode 100644
index 0000000..4b156e6
--- /dev/null
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/jaxb-api-2.3.1/license.txt
@@ -0,0 +1,760 @@
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1
+
+1. Definitions.
+
+    1.1. "Contributor" means each individual or entity that creates or
+    contributes to the creation of Modifications.
+
+    1.2. "Contributor Version" means the combination of the Original
+    Software, prior Modifications used by a Contributor (if any), and
+    the Modifications made by that particular Contributor.
+
+    1.3. "Covered Software" means (a) the Original Software, or (b)
+    Modifications, or (c) the combination of files containing Original
+    Software with files containing Modifications, in each case including
+    portions thereof.
+
+    1.4. "Executable" means the Covered Software in any form other than
+    Source Code.
+
+    1.5. "Initial Developer" means the individual or entity that first
+    makes Original Software available under this License.
+
+    1.6. "Larger Work" means a work which combines Covered Software or
+    portions thereof with code not governed by the terms of this License.
+
+    1.7. "License" means this document.
+
+    1.8. "Licensable" means having the right to grant, to the maximum
+    extent possible, whether at the time of the initial grant or
+    subsequently acquired, any and all of the rights conveyed herein.
+
+    1.9. "Modifications" means the Source Code and Executable form of
+    any of the following:
+
+    A. Any file that results from an addition to, deletion from or
+    modification of the contents of a file containing Original Software
+    or previous Modifications;
+
+    B. Any new file that contains any part of the Original Software or
+    previous Modification; or
+
+    C. Any new file that is contributed or otherwise made available
+    under the terms of this License.
+
+    1.10. "Original Software" means the Source Code and Executable form
+    of computer software code that is originally released under this
+    License.
+
+    1.11. "Patent Claims" means any patent claim(s), now owned or
+    hereafter acquired, including without limitation, method, process,
+    and apparatus claims, in any patent Licensable by grantor.
+
+    1.12. "Source Code" means (a) the common form of computer software
+    code in which modifications are made and (b) associated
+    documentation included in or with such code.
+
+    1.13. "You" (or "Your") means an individual or a legal entity
+    exercising rights under, and complying with all of the terms of,
+    this License. For legal entities, "You" includes any entity which
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants.
+
+    2.1. The Initial Developer Grant.
+
+    Conditioned upon Your compliance with Section 3.1 below and subject
+    to third party intellectual property claims, the Initial Developer
+    hereby grants You a world-wide, royalty-free, non-exclusive license:
+
+    (a) under intellectual property rights (other than patent or
+    trademark) Licensable by Initial Developer, to use, reproduce,
+    modify, display, perform, sublicense and distribute the Original
+    Software (or portions thereof), with or without Modifications,
+    and/or as part of a Larger Work; and
+
+    (b) under Patent Claims infringed by the making, using or selling of
+    Original Software, to make, have made, use, practice, sell, and
+    offer for sale, and/or otherwise dispose of the Original Software
+    (or portions thereof).
+
+    (c) The licenses granted in Sections 2.1(a) and (b) are effective on
+    the date Initial Developer first distributes or otherwise makes the
+    Original Software available to a third party under the terms of this
+    License.
+
+    (d) Notwithstanding Section 2.1(b) above, no patent license is
+    granted: (1) for code that You delete from the Original Software, or
+    (2) for infringements caused by: (i) the modification of the
+    Original Software, or (ii) the combination of the Original Software
+    with other software or devices.
+
+    2.2. Contributor Grant.
+
+    Conditioned upon Your compliance with Section 3.1 below and subject
+    to third party intellectual property claims, each Contributor hereby
+    grants You a world-wide, royalty-free, non-exclusive license:
+
+    (a) under intellectual property rights (other than patent or
+    trademark) Licensable by Contributor to use, reproduce, modify,
+    display, perform, sublicense and distribute the Modifications
+    created by such Contributor (or portions thereof), either on an
+    unmodified basis, with other Modifications, as Covered Software
+    and/or as part of a Larger Work; and
+
+    (b) under Patent Claims infringed by the making, using, or selling
+    of Modifications made by that Contributor either alone and/or in
+    combination with its Contributor Version (or portions of such
+    combination), to make, use, sell, offer for sale, have made, and/or
+    otherwise dispose of: (1) Modifications made by that Contributor (or
+    portions thereof); and (2) the combination of Modifications made by
+    that Contributor with its Contributor Version (or portions of such
+    combination).
+
+    (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective
+    on the date Contributor first distributes or otherwise makes the
+    Modifications available to a third party.
+
+    (d) Notwithstanding Section 2.2(b) above, no patent license is
+    granted: (1) for any code that Contributor has deleted from the
+    Contributor Version; (2) for infringements caused by: (i) third
+    party modifications of Contributor Version, or (ii) the combination
+    of Modifications made by that Contributor with other software
+    (except as part of the Contributor Version) or other devices; or (3)
+    under Patent Claims infringed by Covered Software in the absence of
+    Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+    3.1. Availability of Source Code.
+
+    Any Covered Software that You distribute or otherwise make available
+    in Executable form must also be made available in Source Code form
+    and that Source Code form must be distributed only under the terms
+    of this License. You must include a copy of this License with every
+    copy of the Source Code form of the Covered Software You distribute
+    or otherwise make available. You must inform recipients of any such
+    Covered Software in Executable form as to how they can obtain such
+    Covered Software in Source Code form in a reasonable manner on or
+    through a medium customarily used for software exchange.
+
+    3.2. Modifications.
+
+    The Modifications that You create or to which You contribute are
+    governed by the terms of this License. You represent that You
+    believe Your Modifications are Your original creation(s) and/or You
+    have sufficient rights to grant the rights conveyed by this License.
+
+    3.3. Required Notices.
+
+    You must include a notice in each of Your Modifications that
+    identifies You as the Contributor of the Modification. You may not
+    remove or alter any copyright, patent or trademark notices contained
+    within the Covered Software, or any notices of licensing or any
+    descriptive text giving attribution to any Contributor or the
+    Initial Developer.
+
+    3.4. Application of Additional Terms.
+
+    You may not offer or impose any terms on any Covered Software in
+    Source Code form that alters or restricts the applicable version of
+    this License or the recipients' rights hereunder. You may choose to
+    offer, and to charge a fee for, warranty, support, indemnity or
+    liability obligations to one or more recipients of Covered Software.
+    However, you may do so only on Your own behalf, and not on behalf of
+    the Initial Developer or any Contributor. You must make it
+    absolutely clear that any such warranty, support, indemnity or
+    liability obligation is offered by You alone, and You hereby agree
+    to indemnify the Initial Developer and every Contributor for any
+    liability incurred by the Initial Developer or such Contributor as a
+    result of warranty, support, indemnity or liability terms You offer.
+
+    3.5. Distribution of Executable Versions.
+
+    You may distribute the Executable form of the Covered Software under
+    the terms of this License or under the terms of a license of Your
+    choice, which may contain terms different from this License,
+    provided that You are in compliance with the terms of this License
+    and that the license for the Executable form does not attempt to
+    limit or alter the recipient's rights in the Source Code form from
+    the rights set forth in this License. If You distribute the Covered
+    Software in Executable form under a different license, You must make
+    it absolutely clear that any terms which differ from this License
+    are offered by You alone, not by the Initial Developer or
+    Contributor. You hereby agree to indemnify the Initial Developer and
+    every Contributor for any liability incurred by the Initial
+    Developer or such Contributor as a result of any such terms You offer.
+
+    3.6. Larger Works.
+
+    You may create a Larger Work by combining Covered Software with
+    other code not governed by the terms of this License and distribute
+    the Larger Work as a single product. In such a case, You must make
+    sure the requirements of this License are fulfilled for the Covered
+    Software.
+
+4. Versions of the License.
+
+    4.1. New Versions.
+
+    Oracle is the initial license steward and may publish revised and/or
+    new versions of this License from time to time. Each version will be
+    given a distinguishing version number. Except as provided in Section
+    4.3, no one other than the license steward has the right to modify
+    this License.
+
+    4.2. Effect of New Versions.
+
+    You may always continue to use, distribute or otherwise make the
+    Covered Software available under the terms of the version of the
+    License under which You originally received the Covered Software. If
+    the Initial Developer includes a notice in the Original Software
+    prohibiting it from being distributed or otherwise made available
+    under any subsequent version of the License, You must distribute and
+    make the Covered Software available under the terms of the version
+    of the License under which You originally received the Covered
+    Software. Otherwise, You may also choose to use, distribute or
+    otherwise make the Covered Software available under the terms of any
+    subsequent version of the License published by the license steward.
+
+    4.3. Modified Versions.
+
+    When You are an Initial Developer and You want to create a new
+    license for Your Original Software, You may create and use a
+    modified version of this License if You: (a) rename the license and
+    remove any references to the name of the license steward (except to
+    note that the license differs from this License); and (b) otherwise
+    make it clear that the license contains terms which differ from this
+    License.
+
+5. DISCLAIMER OF WARRANTY.
+
+    COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+    WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+    INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE
+    IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR
+    NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF
+    THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE
+    DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY
+    OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING,
+    REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN
+    ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS
+    AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+6. TERMINATION.
+
+    6.1. This License and the rights granted hereunder will terminate
+    automatically if You fail to comply with terms herein and fail to
+    cure such breach within 30 days of becoming aware of the breach.
+    Provisions which, by their nature, must remain in effect beyond the
+    termination of this License shall survive.
+
+    6.2. If You assert a patent infringement claim (excluding
+    declaratory judgment actions) against Initial Developer or a
+    Contributor (the Initial Developer or Contributor against whom You
+    assert such claim is referred to as "Participant") alleging that the
+    Participant Software (meaning the Contributor Version where the
+    Participant is a Contributor or the Original Software where the
+    Participant is the Initial Developer) directly or indirectly
+    infringes any patent, then any and all rights granted directly or
+    indirectly to You by such Participant, the Initial Developer (if the
+    Initial Developer is not the Participant) and all Contributors under
+    Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice
+    from Participant terminate prospectively and automatically at the
+    expiration of such 60 day notice period, unless if within such 60
+    day period You withdraw Your claim with respect to the Participant
+    Software against such Participant either unilaterally or pursuant to
+    a written agreement with Participant.
+
+    6.3. If You assert a patent infringement claim against Participant
+    alleging that the Participant Software directly or indirectly
+    infringes any patent where such claim is resolved (such as by
+    license or settlement) prior to the initiation of patent
+    infringement litigation, then the reasonable value of the licenses
+    granted by such Participant under Sections 2.1 or 2.2 shall be taken
+    into account in determining the amount or value of any payment or
+    license.
+
+    6.4. In the event of termination under Sections 6.1 or 6.2 above,
+    all end user licenses that have been validly granted by You or any
+    distributor hereunder prior to termination (excluding licenses
+    granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+    (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
+    INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
+    COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE
+    TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+    CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
+    LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER
+    FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR
+    LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE
+    POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT
+    APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH
+    PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH
+    LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR
+    LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION
+    AND LIMITATION MAY NOT APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+    The Covered Software is a "commercial item," as that term is defined
+    in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+    software" (as that term is defined at 48 C.F.R. §
+    252.227-7014(a)(1)) and "commercial computer software documentation"
+    as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent
+    with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4
+    (June 1995), all U.S. Government End Users acquire Covered Software
+    with only those rights set forth herein. This U.S. Government Rights
+    clause is in lieu of, and supersedes, any other FAR, DFAR, or other
+    clause or provision that addresses Government rights in computer
+    software under this License.
+
+9. MISCELLANEOUS.
+
+    This License represents the complete agreement concerning subject
+    matter hereof. If any provision of this License is held to be
+    unenforceable, such provision shall be reformed only to the extent
+    necessary to make it enforceable. This License shall be governed by
+    the law of the jurisdiction specified in a notice contained within
+    the Original Software (except to the extent applicable law, if any,
+    provides otherwise), excluding such jurisdiction's conflict-of-law
+    provisions. Any litigation relating to this License shall be subject
+    to the jurisdiction of the courts located in the jurisdiction and
+    venue specified in a notice contained within the Original Software,
+    with the losing party responsible for costs, including, without
+    limitation, court costs and reasonable attorneys' fees and expenses.
+    The application of the United Nations Convention on Contracts for
+    the International Sale of Goods is expressly excluded. Any law or
+    regulation which provides that the language of a contract shall be
+    construed against the drafter shall not apply to this License. You
+    agree that You alone are responsible for compliance with the United
+    States export administration regulations (and the export control
+    laws and regulation of any other countries) when You use, distribute
+    or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+    As between Initial Developer and the Contributors, each party is
+    responsible for claims and damages arising, directly or indirectly,
+    out of its utilization of rights under this License and You agree to
+    work with Initial Developer and Contributors to distribute such
+    responsibility on an equitable basis. Nothing herein is intended or
+    shall be deemed to constitute any admission of liability.
+
+------------------------------------------------------------------------
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION
+LICENSE (CDDL)
+
+The code released under the CDDL shall be governed by the laws of the
+State of California (excluding conflict-of-law provisions). Any
+litigation relating to this License shall be subject to the jurisdiction
+of the Federal Courts of the Northern District of California and the
+state courts of the State of California, with venue lying in Santa Clara
+County, California.
+
+
+
+  The GNU General Public License (GPL) Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor
+Boston, MA 02110-1335
+USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public
+License applies to most of the Free Software Foundation's software and
+to any other program whose authors commit to using it. (Some other Free
+Software Foundation software is covered by the GNU Library General
+Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this
+service if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must give the recipients all the rights that you have.
+You must make sure that they, too, receive or can get the source code.
+And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a
+notice placed by the copyright holder saying it may be distributed under
+the terms of this General Public License. The "Program", below, refers
+to any such program or work, and a "work based on the Program" means
+either the Program or any derivative work under copyright law: that is
+to say, a work containing the Program or a portion of it, either
+verbatim or with modifications and/or translated into another language.
+(Hereinafter, translation is included without limitation in the term
+"modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of running
+the Program is not restricted, and the output from the Program is
+covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy an appropriate copyright notice
+and disclaimer of warranty; keep intact all the notices that refer to
+this License and to the absence of any warranty; and give any other
+recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of
+it, thus forming a work based on the Program, and copy and distribute
+such modifications or work under the terms of Section 1 above, provided
+that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any part
+    thereof, to be licensed as a whole at no charge to all third parties
+    under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a notice
+    that there is no warranty (or else, saying that you provide a
+    warranty) and that users may redistribute the program under these
+    conditions, and telling the user how to view a copy of this License.
+    (Exception: if the Program itself is interactive but does not
+    normally print such an announcement, your work based on the Program
+    is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program, and
+can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based on
+the Program, the distribution of the whole must be on the terms of this
+License, whose permissions for other licensees extend to the entire
+whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the
+scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections 1
+    and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your cost
+    of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer to
+    distribute corresponding source code. (This alternative is allowed
+    only for noncommercial distribution and only if you received the
+    program in object code or executable form with such an offer, in
+    accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to control
+compilation and installation of the executable. However, as a special
+exception, the source code distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access
+to copy from a designated place, then offering equivalent access to copy
+the source code from the same place counts as distribution of the source
+code, even though third parties are not compelled to copy the source
+along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and will
+automatically terminate your rights under this License. However, parties
+who have received copies, or rights, from you under this License will
+not have their licenses terminated so long as such parties remain in
+full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and all
+its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further restrictions
+on the recipients' exercise of the rights granted herein. You are not
+responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot distribute
+so as to satisfy simultaneously your obligations under this License and
+any other pertinent obligations, then as a consequence you may not
+distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who
+receive copies directly or indirectly through you, then the only way you
+could satisfy both it and this License would be to refrain entirely from
+distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is implemented
+by public license practices. Many people have made generous
+contributions to the wide range of software distributed through that
+system in reliance on consistent application of that system; it is up to
+the author/donor to decide if he or she is willing to distribute
+software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be
+a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License may
+add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among countries
+not thus excluded. In such case, this License incorporates the
+limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new
+versions of the General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a version
+number of this License, you may choose any version ever published by the
+Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the
+author to ask for permission. For software which is copyrighted by the
+Free Software Foundation, write to the Free Software Foundation; we
+sometimes make exceptions for this. Our decision will be guided by the
+two goals of preserving the free status of all derivatives of our free
+software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    One line to give the program's name and a brief idea of what it does.
+    Copyright (C) <year> <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type
+    `show w'. This is free software, and you are welcome to redistribute
+    it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the commands
+you use may be called something other than `show w' and `show c'; they
+could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+    Yoyodyne, Inc., hereby disclaims all copyright interest in the
+    program `Gnomovision' (which makes passes at compilers) written by
+    James Hacker.
+
+    signature of Ty Coon, 1 April 1989
+    Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library. If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+#
+
+Certain source files distributed by Oracle America, Inc. and/or its
+affiliates are subject to the following clarification and special
+exception to the GPLv2, based on the GNU Project exception for its
+Classpath libraries, known as the GNU Classpath Exception, but only
+where Oracle has expressly included in the particular source file's
+header the words "Oracle designates this particular file as subject to
+the "Classpath" exception as provided by Oracle in the LICENSE file
+that accompanied this code."
+
+You should also note that Oracle includes multiple, independent
+programs in this software package. Some of those programs are provided
+under licenses deemed incompatible with the GPLv2 by the Free Software
+Foundation and others.  For example, the package includes programs
+licensed under the Apache License, Version 2.0.  Such programs are
+licensed to you under their original licenses.
+
+Oracle facilitates your further distribution of this package by adding
+the Classpath Exception to the necessary parts of its GPLv2 code, which
+permits you to use that code in combination with other independent
+modules not licensed under the GPLv2.  However, note that this would
+not permit you to commingle code under an incompatible license with
+Oracle's GPLv2 licensed code by, for example, cutting and pasting such
+code into a file also containing Oracle's GPLv2 licensed code and then
+distributing the result.  Additionally, if you were to remove the
+Classpath Exception from any of the files to which it applies and
+distribute the result, you would likely be required to license some or
+all of the other code in that distribution under the GPLv2 as well, and
+since the GPLv2 is incompatible with the license terms of some items
+included in the distribution by Oracle, removing the Classpath
+Exception could therefore effectively compromise your ability to
+further distribute the package.
+
+Proceed with caution and we recommend that you obtain the advice of a
+lawyer skilled in open source matters before removing the Classpath
+Exception or making modifications to this package which may
+subsequently be redistributed and/or involve the use of third party
+software.
+
+CLASSPATH EXCEPTION
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License version 2 cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from or
+based on this library.  If you modify this library, you may extend this
+exception to your version of the library, but you are not obligated to
+do so.  If you do not wish to do so, delete this exception statement
+from your version.
+
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 d80b594..639b7fc 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
@@ -22,6 +22,7 @@
         "translations/en.json",
         "translations/es.json",
         "translations/fr.json",
+        "translations/ja.json",
         "translations/ru.json"
     ]
 
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 6b2d17c..ce59c77 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
@@ -22,6 +22,7 @@
         "translations/en.json",
         "translations/es.json",
         "translations/fr.json",
+        "translations/ja.json",
         "translations/ru.json"
     ]
 
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 79286f1..ba65007 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
@@ -22,6 +22,7 @@
         "translations/en.json",
         "translations/es.json",
         "translations/fr.json",
+        "translations/ja.json",
         "translations/ru.json"
     ]
 
diff --git a/extensions/guacamole-auth-openid/pom.xml b/extensions/guacamole-auth-openid/pom.xml
index 967ad03..25158c8 100644
--- a/extensions/guacamole-auth-openid/pom.xml
+++ b/extensions/guacamole-auth-openid/pom.xml
@@ -246,6 +246,14 @@
             <version>2.5</version>
             <scope>provided</scope>
         </dependency>
+        
+        <!-- Jersey - JAX-RS Implementation -->
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+            <version>1.1.1</version>
+            <scope>provided</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java
index c742d89..9d889a8 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/conf/ConfigurationService.java
@@ -20,10 +20,12 @@
 package org.apache.guacamole.auth.openid.conf;
 
 import com.google.inject.Inject;
+import java.net.URI;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.properties.IntegerGuacamoleProperty;
 import org.apache.guacamole.properties.StringGuacamoleProperty;
+import org.apache.guacamole.properties.URIGuacamoleProperty;
 
 /**
  * Service for retrieving configuration information regarding the OpenID
@@ -63,8 +65,8 @@
     /**
      * The authorization endpoint (URI) of the OpenID service.
      */
-    private static final StringGuacamoleProperty OPENID_AUTHORIZATION_ENDPOINT =
-            new StringGuacamoleProperty() {
+    private static final URIGuacamoleProperty OPENID_AUTHORIZATION_ENDPOINT =
+            new URIGuacamoleProperty() {
 
         @Override
         public String getName() { return "openid-authorization-endpoint"; }
@@ -75,8 +77,8 @@
      * The endpoint (URI) of the JWKS service which defines how received ID
      * tokens (JWTs) shall be validated.
      */
-    private static final StringGuacamoleProperty OPENID_JWKS_ENDPOINT =
-            new StringGuacamoleProperty() {
+    private static final URIGuacamoleProperty OPENID_JWKS_ENDPOINT =
+            new URIGuacamoleProperty() {
 
         @Override
         public String getName() { return "openid-jwks-endpoint"; }
@@ -174,8 +176,8 @@
      * authentication process is complete. This must be the full URL that a
      * user would enter into their browser to access Guacamole.
      */
-    private static final StringGuacamoleProperty OPENID_REDIRECT_URI =
-            new StringGuacamoleProperty() {
+    private static final URIGuacamoleProperty OPENID_REDIRECT_URI =
+            new URIGuacamoleProperty() {
 
         @Override
         public String getName() { return "openid-redirect-uri"; }
@@ -200,7 +202,7 @@
      *     If guacamole.properties cannot be parsed, or if the authorization
      *     endpoint property is missing.
      */
-    public String getAuthorizationEndpoint() throws GuacamoleException {
+    public URI getAuthorizationEndpoint() throws GuacamoleException {
         return environment.getRequiredProperty(OPENID_AUTHORIZATION_ENDPOINT);
     }
 
@@ -236,7 +238,7 @@
      *     If guacamole.properties cannot be parsed, or if the redirect URI
      *     property is missing.
      */
-    public String getRedirectURI() throws GuacamoleException {
+    public URI getRedirectURI() throws GuacamoleException {
         return environment.getRequiredProperty(OPENID_REDIRECT_URI);
     }
 
@@ -270,7 +272,7 @@
      *     If guacamole.properties cannot be parsed, or if the JWKS endpoint
      *     property is missing.
      */
-    public String getJWKSEndpoint() throws GuacamoleException {
+    public URI getJWKSEndpoint() throws GuacamoleException {
         return environment.getRequiredProperty(OPENID_JWKS_ENDPOINT);
     }
 
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java
index d99c367..b6ca18b 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/form/TokenField.java
@@ -19,8 +19,8 @@
 
 package org.apache.guacamole.auth.openid.form;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
+import java.net.URI;
+import javax.ws.rs.core.UriBuilder;
 import org.apache.guacamole.form.Field;
 
 /**
@@ -38,7 +38,7 @@
     /**
      * The full URI which the field should link to.
      */
-    private final String authorizationURI;
+    private final URI authorizationURI;
 
     /**
      * Creates a new field which requests authentication via OpenID connect.
@@ -69,26 +69,19 @@
      *     A random string unique to this request. To defend against replay
      *     attacks, this value must cease being valid after its first use.
      */
-    public TokenField(String authorizationEndpoint, String scope,
-            String clientID, String redirectURI, String nonce) {
+    public TokenField(URI authorizationEndpoint, String scope,
+            String clientID, URI redirectURI, String nonce) {
 
         // Init base field properties
         super(PARAMETER_NAME, "GUAC_OPENID_TOKEN");
 
-        // Build authorization URI from given values
-        try {
-            this.authorizationURI = authorizationEndpoint
-                    + "?scope=" + URLEncoder.encode(scope, "UTF-8")
-                    + "&response_type=id_token"
-                    + "&client_id=" + URLEncoder.encode(clientID, "UTF-8")
-                    + "&redirect_uri=" + URLEncoder.encode(redirectURI, "UTF-8")
-                    + "&nonce=" + nonce;
-        }
-
-        // Java is required to provide UTF-8 support
-        catch (UnsupportedEncodingException e) {
-            throw new UnsupportedOperationException("Unexpected lack of UTF-8 support.", e);
-        }
+        this.authorizationURI = UriBuilder.fromUri(authorizationEndpoint)
+                .queryParam("scope", scope)
+                .queryParam("response_type", "id_token")
+                .queryParam("client_id", clientID)
+                .queryParam("redirect_uri", redirectURI)
+                .queryParam("nonce", nonce)
+                .build();
 
     }
 
@@ -100,7 +93,7 @@
      *     The full URI that this field should link to.
      */
     public String getAuthorizationURI() {
-        return authorizationURI;
+        return authorizationURI.toString();
     }
 
 }
diff --git a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java
index cde4f89..5efb09d 100644
--- a/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java
+++ b/extensions/guacamole-auth-openid/src/main/java/org/apache/guacamole/auth/openid/token/TokenValidationService.java
@@ -74,7 +74,7 @@
     public String processUsername(String token) throws GuacamoleException {
 
         // Validating the token requires a JWKS key resolver
-        HttpsJwks jwks = new HttpsJwks(confService.getJWKSEndpoint());
+        HttpsJwks jwks = new HttpsJwks(confService.getJWKSEndpoint().toString());
         HttpsJwksVerificationKeyResolver resolver = new HttpsJwksVerificationKeyResolver(jwks);
 
         // Create JWT consumer for validating received token
diff --git a/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js b/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js
index 12bc0da..5d0b6b2 100644
--- a/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js
+++ b/extensions/guacamole-auth-openid/src/main/resources/config/openidConfig.js
@@ -31,24 +31,3 @@
     });
 
 }]);
-
-/**
- * Config block which augments the existing routing, providing special handling
- * for the "id_token=" fragments provided by OpenID Connect.
- */
-angular.module('index').config(['$routeProvider',
-        function indexRouteConfig($routeProvider) {
-
-    // Transform "/#/id_token=..." to "/#/?id_token=..."
-    $routeProvider.when('/id_token=:response', {
-
-        template   : '',
-        controller : ['$location', function reroute($location) {
-            var params = $location.path().substring(1);
-            $location.url('/');
-            $location.search(params);
-        }]
-
-    });
-
-}]);
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 4bac126..b15f83f 100644
--- a/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-openid/src/main/resources/guac-manifest.json
@@ -10,7 +10,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "js" : [
diff --git a/extensions/guacamole-auth-openid/src/main/resources/transformToken.js b/extensions/guacamole-auth-openid/src/main/resources/transformToken.js
new file mode 100644
index 0000000..b65d2fd
--- /dev/null
+++ b/extensions/guacamole-auth-openid/src/main/resources/transformToken.js
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/**
+ * Before AngularJS routing takes effect, reformat the URL fragment
+ * from the format used by OpenID Connect ("#param1=value1&param2=value2&...")
+ * to the format used by AngularJS ("#/?param1=value1&param2=value2&...") such
+ * that the client side of Guacamole's authentication system will automatically
+ * forward the "id_token" value for server-side validation.
+ * 
+ * Note that not all OpenID identity providers will include the "id_token"
+ * parameter in the first position; it may occur after several other parameters
+ * within the fragment.
+ */
+(function guacOpenIDTransformToken() {
+    if (/^#(?![?\/])(.*&)?id_token=/.test(location.hash))
+        location.hash = '/?' + location.hash.substring(1);
+})();
diff --git a/extensions/guacamole-auth-openid/src/main/resources/translations/ja.json b/extensions/guacamole-auth-openid/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..9a5b518
--- /dev/null
+++ b/extensions/guacamole-auth-openid/src/main/resources/translations/ja.json
@@ -0,0 +1,7 @@
+{
+
+    "LOGIN" : {
+        "INFO_REDIRECT_PENDING" : "IDプロバイダへリダイレクトしています。"
+    }
+
+}
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 10c4f8a..48eeb42 100644
--- a/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-quickconnect/src/main/resources/guac-manifest.json
@@ -21,7 +21,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "resources" : {
diff --git a/extensions/guacamole-auth-quickconnect/src/main/resources/translations/ja.json b/extensions/guacamole-auth-quickconnect/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..4d44d4b
--- /dev/null
+++ b/extensions/guacamole-auth-quickconnect/src/main/resources/translations/ja.json
@@ -0,0 +1,14 @@
+{
+
+    "QUICKCONNECT" : {
+        "ACTION_CONNECT"        : "接続",
+        
+        "ERROR_INVALID_URI"      : "URIが無効です",
+        "ERROR_NO_HOST"          : "ホストが指定されていません",
+        "ERROR_NO_PROTOCOL"      : "プロトコルが指定されていません",
+        "ERROR_NOT_ABSOLUTE_URI" : "絶対URIで指定してください",
+        
+        "FIELD_PLACEHOLDER_URI" : "接続するURIを入力"
+    }
+
+}
diff --git a/extensions/guacamole-auth-radius/pom.xml b/extensions/guacamole-auth-radius/pom.xml
index 73c332a..abdb2d3 100644
--- a/extensions/guacamole-auth-radius/pom.xml
+++ b/extensions/guacamole-auth-radius/pom.xml
@@ -189,6 +189,13 @@
             <version>1.1.0</version>
             <scope>provided</scope>
         </dependency>
+        
+        <!-- Guava - Utility Library -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.0.1-jre</version>
+        </dependency>
 
         <!-- Guice -->
         <dependency>
diff --git a/extensions/guacamole-auth-radius/src/licenses/LICENSE b/extensions/guacamole-auth-radius/src/licenses/LICENSE
index 7ad231d..cb58a78 100644
--- a/extensions/guacamole-auth-radius/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-radius/src/licenses/LICENSE
@@ -238,6 +238,15 @@
         Apache v2.0 (bundled/guice-3.0/COPYING)
 
 
+Guava: Google Core Libraries for Java (https://github.com/google/guava)
+-----------------------------------------------------------------------
+
+    Version: 27.0.1-jre
+    From: 'Google Inc.' (http://www.google.com/)
+    License(s):
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
+
+
 JRadius (https://github.com/coova/jradius)
 ------------------------------------------
 
diff --git a/extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING b/extensions/guacamole-auth-radius/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
copy from extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING
copy to extensions/guacamole-auth-radius/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
index 852eb72..fee4357 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/AuthenticationProviderService.java
@@ -19,15 +19,13 @@
 
 package org.apache.guacamole.auth.radius;
 
+import com.google.common.io.BaseEncoding;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import java.lang.IllegalArgumentException;
-import java.nio.charset.Charset;
 import java.util.Arrays;
 import javax.servlet.http.HttpServletRequest;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.auth.radius.user.AuthenticatedUser;
-import org.apache.guacamole.auth.radius.form.RadiusChallengeResponseField;
+import org.apache.guacamole.auth.radius.form.GuacamoleRadiusChallenge;
 import org.apache.guacamole.auth.radius.form.RadiusStateField;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.form.Field;
@@ -44,6 +42,7 @@
 import net.jradius.packet.AccessChallenge;
 import net.jradius.packet.AccessReject;
 import net.jradius.packet.attribute.RadiusAttribute;
+import org.apache.guacamole.form.PasswordField;
 
 /**
  * Service providing convenience functions for the RADIUS AuthenticationProvider
@@ -55,6 +54,12 @@
      * Logger for this class.
      */
     private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class);
+    
+    /**
+     * The name of the password field where the user will enter a response to
+     * the RADIUS challenge.
+     */
+    private static final String CHALLENGE_RESPONSE_PARAM = "radiusChallenge";
 
     /**
      * Service for creating and managing connections to RADIUS servers.
@@ -69,18 +74,23 @@
     private Provider<AuthenticatedUser> authenticatedUserProvider;
 
     /**
-     * Returns the expected credentials from a RADIUS challenge.
+     * Returns an object containing the challenge message and the expected
+     * credentials from a RADIUS challenge, or null if either state or reply
+     * attributes are missing from the challenge.
      *
      * @param challengePacket
      *     The AccessChallenge RadiusPacket received from the RADIUS 
      *     server.
      *
      * @return
-     *     A CredentialsInfo object that represents fields that need to
-     *     be presented to the user in order to complete authentication.
-     *     One of these must be the RADIUS state.
+     *     A GuacamoleRadiusChallenge object that contains the challenge message
+     *     sent by the RADIUS server and the expected credentials that should
+     *     be requested of the user in order to continue authentication.  One
+     *     of the expected credentials *must* be the RADIUS state.  If either
+     *     state or the reply are missing from the challenge this method will
+     *     return null.
      */
-    private CredentialsInfo getRadiusChallenge(RadiusPacket challengePacket) {
+    private GuacamoleRadiusChallenge getRadiusChallenge(RadiusPacket challengePacket) {
 
         // Try to get the state attribute - if it's not there, we have a problem
         RadiusAttribute stateAttr = challengePacket.findAttribute(Attr_State.TYPE);
@@ -99,13 +109,16 @@
         }
 
         // We have the required attributes - convert to strings and then generate the additional login box/field
-        String replyMsg = replyAttr.toString();
-        String radiusState = DatatypeConverter.printHexBinary(stateAttr.getValue().getBytes());
-        Field radiusResponseField = new RadiusChallengeResponseField(replyMsg);
+        String replyMsg = replyAttr.getValue().toString();
+        String radiusState = BaseEncoding.base16().encode(stateAttr.getValue().getBytes());
+        Field radiusResponseField = new PasswordField(CHALLENGE_RESPONSE_PARAM);
         Field radiusStateField = new RadiusStateField(radiusState);
 
-        // Return the CredentialsInfo object that has the state and the expected response.
-        return new CredentialsInfo(Arrays.asList(radiusResponseField,radiusStateField));
+        // Return the GuacamoleRadiusChallenge object that has the state
+        // and the expected response.
+        return new GuacamoleRadiusChallenge(replyMsg,
+                new CredentialsInfo(Arrays.asList(radiusResponseField,
+                        radiusStateField)));
     }
 
     /**
@@ -136,7 +149,7 @@
 
         // Grab HTTP request object and a response to a challenge.
         HttpServletRequest request = credentials.getRequest();
-        String challengeResponse = request.getParameter(RadiusChallengeResponseField.PARAMETER_NAME);
+        String challengeResponse = request.getParameter(CHALLENGE_RESPONSE_PARAM);
 
         // RadiusPacket object to store response from server.
         RadiusPacket radPack;
@@ -164,7 +177,7 @@
                     throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
                 }
 
-                byte[] stateBytes = DatatypeConverter.parseHexBinary(stateString);
+                byte[] stateBytes = BaseEncoding.base16().decode(stateString);
                 radPack = radiusService.sendChallengeResponse(credentials.getUsername(),
                                                               challengeResponse,
                                                               stateBytes);
@@ -202,12 +215,14 @@
 
         // Received AccessChallenge packet, more credentials required to complete authentication
         else if (radPack instanceof AccessChallenge) {
-            CredentialsInfo expectedCredentials = getRadiusChallenge(radPack);
+            GuacamoleRadiusChallenge challenge = getRadiusChallenge(radPack);
 
-            if (expectedCredentials == null)
+            if (challenge == null)
                 throw new GuacamoleInvalidCredentialsException("Authentication error.", CredentialsInfo.USERNAME_PASSWORD);
 
-            throw new GuacamoleInsufficientCredentialsException("LOGIN.INFO_RADIUS_ADDL_REQUIRED", expectedCredentials);
+            throw new GuacamoleInsufficientCredentialsException(
+                    challenge.getChallengeText(),
+                    challenge.getExpectedCredentials());
         }
 
         // Something unanticipated happened, so panic and go back to login.
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
index 37ecb79..4224f77 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusAuthenticationProviderModule.java
@@ -20,10 +20,17 @@
 package org.apache.guacamole.auth.radius;
 
 import com.google.inject.AbstractModule;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.auth.radius.conf.ConfigurationService;
+import org.apache.guacamole.auth.radius.conf.RadiusAuthenticationProtocol;
+import org.apache.guacamole.auth.radius.conf.RadiusGuacamoleProperties;
 import org.apache.guacamole.environment.Environment;
 import org.apache.guacamole.environment.LocalEnvironment;
 import org.apache.guacamole.net.auth.AuthenticationProvider;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
 /**
  * Guice module which configures RADIUS-specific injections.
@@ -57,6 +64,23 @@
 
         // Get local environment
         this.environment = new LocalEnvironment();
+        
+        // Check for MD4 requirement
+        RadiusAuthenticationProtocol authProtocol = environment.getProperty(RadiusGuacamoleProperties.RADIUS_AUTH_PROTOCOL);
+        RadiusAuthenticationProtocol innerProtocol = environment.getProperty(RadiusGuacamoleProperties.RADIUS_EAP_TTLS_INNER_PROTOCOL);
+        if (authProtocol == RadiusAuthenticationProtocol.MSCHAPv1 
+                    || authProtocol == RadiusAuthenticationProtocol.MSCHAPv2
+                    || innerProtocol == RadiusAuthenticationProtocol.MSCHAPv1 
+                    || innerProtocol == RadiusAuthenticationProtocol.MSCHAPv2) {
+            
+            try {
+                MessageDigest.getInstance("MD4");
+            }
+            catch (NoSuchAlgorithmException e) {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+            
+        }
 
         // Store associated auth provider
         this.authProvider = authProvider;
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
index ec82a63..c8a21d6 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusConnectionService.java
@@ -27,6 +27,8 @@
 import java.security.NoSuchAlgorithmException;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.auth.radius.conf.ConfigurationService;
+import org.apache.guacamole.auth.radius.conf.RadiusAuthenticationProtocol;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import net.jradius.client.RadiusClient;
@@ -62,8 +64,7 @@
      */
     @Inject
     private ConfigurationService confService;
-
-
+    
     /**
      * Creates a new instance of RadiusClient, configured with parameters
      * from guacamole.properties.
@@ -115,8 +116,8 @@
      *     not configured when the client is set up for a tunneled
      *     RADIUS connection.
      */
-    private RadiusAuthenticator setupRadiusAuthenticator(RadiusClient radiusClient)
-            throws GuacamoleException {
+    private RadiusAuthenticator setupRadiusAuthenticator(
+            RadiusClient radiusClient) throws GuacamoleException {
 
         // If we don't have a radiusClient object, yet, don't go any further.
         if (radiusClient == null) {
@@ -125,7 +126,9 @@
             return null;
         }
 
-        RadiusAuthenticator radAuth = radiusClient.getAuthProtocol(confService.getRadiusAuthProtocol());
+        RadiusAuthenticator radAuth = radiusClient.getAuthProtocol(
+                confService.getRadiusAuthProtocol().toString());
+        
         if (radAuth == null)
             throw new GuacamoleException("Could not get a valid RadiusAuthenticator for specified protocol: " + confService.getRadiusAuthProtocol());
 
@@ -157,11 +160,13 @@
 
         // If we're using EAP-TTLS, we need to define tunneled protocol
         if (radAuth instanceof EAPTTLSAuthenticator) {
-            String innerProtocol = confService.getRadiusEAPTTLSInnerProtocol();
+            RadiusAuthenticationProtocol innerProtocol =
+                    confService.getRadiusEAPTTLSInnerProtocol();
+            
             if (innerProtocol == null)
-                throw new GuacamoleException("Trying to use EAP-TTLS, but no inner protocol specified.");
+                throw new GuacamoleException("Missing or invalid inner protocol for EAP-TTLS.");
 
-            ((EAPTTLSAuthenticator)radAuth).setInnerProtocol(innerProtocol);
+            ((EAPTTLSAuthenticator)radAuth).setInnerProtocol(innerProtocol.toString());
         }
 
         return radAuth;
@@ -236,14 +241,21 @@
 
             radAuth.setupRequest(radiusClient, radAcc);
             radAuth.processRequest(radAcc);
-            RadiusResponse reply = radiusClient.sendReceive(radAcc, confService.getRadiusMaxRetries());
+            RadiusResponse reply = radiusClient.sendReceive(radAcc,
+                    confService.getRadiusMaxRetries());
 
             // We receive a Challenge not asking for user input, so silently process the challenge
-            while((reply instanceof AccessChallenge) && (reply.findAttribute(Attr_ReplyMessage.TYPE) == null)) {
+            while((reply instanceof AccessChallenge) 
+                    && (reply.findAttribute(Attr_ReplyMessage.TYPE) == null)) {
+                
                 radAuth.processChallenge(radAcc, reply);
-                reply = radiusClient.sendReceive(radAcc, confService.getRadiusMaxRetries());
+                reply = radiusClient.sendReceive(radAcc,
+                        confService.getRadiusMaxRetries());
+                
             }
+            
             return reply;
+            
         }
         catch (RadiusException e) {
             logger.error("Unable to complete authentication.", e.getMessage());
@@ -282,8 +294,8 @@
      * @throws GuacamoleException
      *     If an error is encountered trying to talk to the RADIUS server.
      */
-    public RadiusPacket sendChallengeResponse(String username, String response, byte[] state)
-            throws GuacamoleException {
+    public RadiusPacket sendChallengeResponse(String username, String response,
+            byte[] state) throws GuacamoleException {
 
         if (username == null || username.isEmpty()) {
             logger.error("Challenge/response to RADIUS requires a username.");
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
similarity index 92%
rename from extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java
rename to extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
index 381ea13..2809f7c 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/ConfigurationService.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/ConfigurationService.java
@@ -17,11 +17,12 @@
  * under the License.
  */
 
-package org.apache.guacamole.auth.radius;
+package org.apache.guacamole.auth.radius.conf;
 
 import com.google.inject.Inject;
 import java.io.File;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
 import org.apache.guacamole.environment.Environment;
 
 /**
@@ -123,8 +124,9 @@
      * @throws GuacamoleException
      *     If guacamole.properties cannot be parsed.
      */
-    public String getRadiusAuthProtocol() throws GuacamoleException {
-        return environment.getProperty(
+    public RadiusAuthenticationProtocol getRadiusAuthProtocol()
+            throws GuacamoleException {
+        return environment.getRequiredProperty(
             RadiusGuacamoleProperties.RADIUS_AUTH_PROTOCOL
         );
     }
@@ -309,12 +311,21 @@
      *     an EAP-TTLS RADIUS connection. 
      *     
      * @throws GuacamoleException
-     *     If guacamole.properties cannot be parsed.
+     *     If guacamole.properties cannot be parsed, or if EAP-TTLS is specified
+     *     as the inner protocol.
      */
-    public String getRadiusEAPTTLSInnerProtocol() throws GuacamoleException {
-        return environment.getProperty(
+    public RadiusAuthenticationProtocol getRadiusEAPTTLSInnerProtocol()
+            throws GuacamoleException {
+        
+        RadiusAuthenticationProtocol authProtocol = environment.getProperty(
             RadiusGuacamoleProperties.RADIUS_EAP_TTLS_INNER_PROTOCOL
         );
+        
+        if (authProtocol == RadiusAuthenticationProtocol.EAP_TTLS)
+            throw new GuacamoleServerException("Invalid inner protocol specified for EAP-TTLS.");
+        
+        return authProtocol;
+        
     }
 
 }
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java
new file mode 100644
index 0000000..e64a695
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocol.java
@@ -0,0 +1,118 @@
+/*
+ * 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.auth.radius.conf;
+
+/**
+ * This enum represents supported RADIUS authentication protocols for
+ * the guacamole-auth-radius extension.
+ */
+public enum RadiusAuthenticationProtocol {
+    
+    /**
+     * Password Authentication Protocol (PAP)
+     */
+    PAP("pap"),
+    
+    /**
+     * Challenge-Handshake Authentication Protocol (CHAP)
+     */
+    CHAP("chap"),
+    
+    /**
+     * Microsoft implementation of CHAP, Version 1 (MS-CHAPv1)
+     */
+    MSCHAPv1("mschapv1"),
+    
+    /**
+     * Microsoft implementation of CHAP, Version 2 (MS-CHAPv2)
+     */
+    MSCHAPv2("mschapv2"),
+    
+    /**
+     * Extensible Authentication Protocol (EAP) with MD5 Hashing (EAP-MD5)
+     */
+    EAP_MD5("eap-md5"),
+
+    /**
+     * Extensible Authentication Protocol (EAP) with TLS encryption (EAP-TLS).
+     */
+    EAP_TLS("eap-tls"),
+
+    /**
+     * Extensible Authentication Protocol (EAP) with Tunneled TLS (EAP-TTLS).
+     */
+    EAP_TTLS("eap-ttls");
+
+    /**
+     * This variable stores the string value of the protocol, and is also
+     * used within the extension to pass to JRadius for configuring the
+     * library to talk to the RADIUS server.
+     */
+    private final String strValue;
+    
+    /**
+     * Create a new RadiusAuthenticationProtocol object having the
+     * given string value.
+     * 
+     * @param strValue
+     *     The value of the protocol to store as a string, which will be used
+     *     in specifying the protocol within the guacamole.properties file, and
+     *     will also be used by the JRadius library for its configuration.
+     */
+    RadiusAuthenticationProtocol(String strValue) {
+        this.strValue = strValue;
+    }
+    
+    /**
+    * {@inheritDoc}
+    * <p>
+    * This function returns the stored string values of the selected RADIUS
+    * protocol, which is used both in Guacamole configuration and also to pass
+    * on to the JRadius library for its configuration.
+    * 
+    * @return
+    *     The string value stored for the selected RADIUS protocol.
+    */
+    @Override
+    public String toString() {
+        return strValue;
+    }
+    
+    /**
+     * For a given String value, return the enum value that matches that string,
+     * or null if no matchi is found.
+     * 
+     * @param value
+     *     The string value to search for in the list of enums.
+     * 
+     * @return
+     *     The RadiusAuthenticationProtocol value that is identified by the
+     *     provided String value.
+     */
+    public static RadiusAuthenticationProtocol getEnum(String value) {
+    
+        for (RadiusAuthenticationProtocol v : values())
+            if(v.toString().equals(value))
+                return v;
+        
+        return null;
+    }
+    
+}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java
new file mode 100644
index 0000000..c92c0a3
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusAuthenticationProtocolProperty.java
@@ -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.
+ */
+
+package org.apache.guacamole.auth.radius.conf;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.properties.GuacamoleProperty;
+
+/**
+ * A GuacamoleProperty whose value is a RadiusAuthenticationProtocol.
+ */
+public abstract class RadiusAuthenticationProtocolProperty
+        implements GuacamoleProperty<RadiusAuthenticationProtocol> {
+    
+    @Override
+    public RadiusAuthenticationProtocol parseValue(String value)
+            throws GuacamoleException {
+        
+        // Nothing provided, nothing returned
+        if (value == null)
+            return null;
+        
+        // Attempt to parse the string value
+        RadiusAuthenticationProtocol authProtocol = 
+                RadiusAuthenticationProtocol.getEnum(value);
+        
+        // Throw an exception if nothing matched.
+        if (authProtocol == null)
+            throw new GuacamoleServerException(
+                    "Invalid or unsupported RADIUS authentication protocol.");
+        
+        // Return the answer
+        return authProtocol;
+        
+    }
+    
+}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
similarity index 93%
rename from extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java
rename to extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
index aaa445e..af6839b 100644
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/RadiusGuacamoleProperties.java
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/conf/RadiusGuacamoleProperties.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.guacamole.auth.radius;
+package org.apache.guacamole.auth.radius.conf;
 
 import org.apache.guacamole.properties.BooleanGuacamoleProperty;
 import org.apache.guacamole.properties.FileGuacamoleProperty;
@@ -81,7 +81,8 @@
     /**
      * The authentication protocol of the RADIUS server to connect to when authenticating users.
      */
-    public static final StringGuacamoleProperty RADIUS_AUTH_PROTOCOL = new StringGuacamoleProperty() {
+    public static final RadiusAuthenticationProtocolProperty RADIUS_AUTH_PROTOCOL =
+            new RadiusAuthenticationProtocolProperty() {
 
         @Override
         public String getName() { return "radius-auth-protocol"; }
@@ -181,7 +182,8 @@
     /**
      * The tunneled protocol to use inside a RADIUS EAP-TTLS connection.
      */
-    public static final StringGuacamoleProperty RADIUS_EAP_TTLS_INNER_PROTOCOL = new StringGuacamoleProperty() {
+    public static final RadiusAuthenticationProtocolProperty RADIUS_EAP_TTLS_INNER_PROTOCOL =
+            new RadiusAuthenticationProtocolProperty() {
 
         @Override
         public String getName() { return "radius-eap-ttls-inner-protocol"; }
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/GuacamoleRadiusChallenge.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/GuacamoleRadiusChallenge.java
new file mode 100644
index 0000000..4589794
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/GuacamoleRadiusChallenge.java
@@ -0,0 +1,77 @@
+/*
+ * 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.auth.radius.form;
+
+import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
+
+/**
+ * Stores the RADIUS challenge message and expected credentials in a single
+ * object.
+ */
+public class GuacamoleRadiusChallenge {
+    
+    /**
+     * The challenge text sent by the RADIUS server.
+     */
+    private final String challengeText;
+    
+    /**
+     * The expected credentials that need to be provided to satisfy the
+     * RADIUS authentication challenge.
+     */
+    private final CredentialsInfo expectedCredentials;
+    
+    /**
+     * Creates a new GuacamoleRadiusChallenge object with the provided
+     * challenge message and expected credentials.
+     * 
+     * @param challengeText
+     *     The challenge message sent by the RADIUS server.
+     * 
+     * @param expectedCredentials 
+     *     The credentials required to complete the challenge.
+     */
+    public GuacamoleRadiusChallenge(String challengeText,
+            CredentialsInfo expectedCredentials) {
+        this.challengeText = challengeText;
+        this.expectedCredentials = expectedCredentials;
+    }
+    
+    /**
+     * Returns the challenge message provided by the RADIUS server.
+     * 
+     * @return
+     *     The challenge message provided by the RADIUS server.
+     */
+    public String getChallengeText() {
+        return challengeText;
+    }
+    
+    /**
+     * Returns the credentials required to satisfy the RADIUS challenge.
+     * 
+     * @return 
+     *     The credentials required to satisfy the RADIUS challenge.
+     */
+    public CredentialsInfo getExpectedCredentials() {
+        return expectedCredentials;
+    }
+    
+}
diff --git a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java b/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java
deleted file mode 100644
index 32ceb90..0000000
--- a/extensions/guacamole-auth-radius/src/main/java/org/apache/guacamole/auth/radius/form/RadiusChallengeResponseField.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.auth.radius.form;
-
-import org.apache.guacamole.form.Field;
-
-/**
- * A form used to prompt the user for additional information when
- * the RADIUS server sends a challenge back to the user with a reply
- * message.
- */
-public class RadiusChallengeResponseField extends Field {
-
-    /**
-     * The field returned by the RADIUS challenge/response.
-     */
-    public static final String PARAMETER_NAME = "guac-radius-challenge-response";
-
-    /**
-     * The type of field to initialize for the challenge/response.
-     */
-    private static final String RADIUS_FIELD_TYPE = "GUAC_RADIUS_CHALLENGE_RESPONSE";
-
-    /**
-     * The message the RADIUS server sent back in the challenge.
-     */
-    private final String challenge;
-
-    /**
-     * Initialize the field with the challenge sent back by the RADIUS server.
-     *
-     * @param challenge
-     *     The challenge message sent back by the RADIUS server.
-     */
-    public RadiusChallengeResponseField(String challenge) {
-        super(PARAMETER_NAME, RADIUS_FIELD_TYPE);
-        this.challenge = challenge;
-
-    }
-
-    /**
-     * Get the challenge sent by the RADIUS server.
-     *
-     * @return
-     *     A String that indicates the challenge returned
-     *     by the RADIUS server.
-     */
-    public String getChallenge() {
-        return challenge;
-    }
-}
diff --git a/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js b/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
index dab0ffc..a3d72bf 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
+++ b/extensions/guacamole-auth-radius/src/main/resources/config/radiusConfig.js
@@ -23,13 +23,6 @@
 angular.module('guacRadius').config(['formServiceProvider',
         function guacRadiusConfig(formServiceProvider) {
 
-    // Define field for the challenge from the RADIUS service
-    formServiceProvider.registerFieldType('GUAC_RADIUS_CHALLENGE_RESPONSE', {
-        module      : 'guacRadius',
-        controller  : 'radiusResponseController',
-        templateUrl : 'app/ext/radius/templates/radiusResponseField.html'
-    });
-
     // Define the hidden field for the RADIUS state
     formServiceProvider.registerFieldType('GUAC_RADIUS_STATE', {
         module      : 'guacRadius',
diff --git a/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js b/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js
deleted file mode 100644
index 4782b20..0000000
--- a/extensions/guacamole-auth-radius/src/main/resources/controllers/radiusResponseController.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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 "GUAC_RADIUS_CHALLENGE_RESPONSE" field which
- * passes the RADIUS server challenge to the user and takes the response.
- */
-angular.module('guacRadius').controller('radiusResponseController', ['$scope', '$injector',
-        function radiusResponseController($scope, $injector) {
-
-    // Populate the reply message field
-    $scope.radiusPlaceholder = $scope.field.challenge;
-
-}]);
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 ac1116b..707f233 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-radius/src/main/resources/guac-manifest.json
@@ -10,7 +10,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "js" : [
@@ -18,7 +19,6 @@
     ],
 
     "resources" : {
-        "templates/radiusResponseField.html" : "text/html",
         "templates/radiusStateField.html"    : "text/html"
     }
 
diff --git a/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html b/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html
deleted file mode 100644
index eec760f..0000000
--- a/extensions/guacamole-auth-radius/src/main/resources/templates/radiusResponseField.html
+++ /dev/null
@@ -1 +0,0 @@
-<input type="password" ng-model="model" ng-trim="false" autocorrect="off" autocapitalize="off" placeholder="{{radiusPlaceholder}}" />
diff --git a/extensions/guacamole-auth-radius/src/main/resources/translations/en.json b/extensions/guacamole-auth-radius/src/main/resources/translations/en.json
index c068a70..1ba2623 100644
--- a/extensions/guacamole-auth-radius/src/main/resources/translations/en.json
+++ b/extensions/guacamole-auth-radius/src/main/resources/translations/en.json
@@ -5,9 +5,8 @@
     },
 
     "LOGIN" : {
-        "FIELD_HEADER_GUAC_RADIUS_CHALLENGE_RESPONSE" : "",
         "FIELD_HEADER_GUAC_RADIUS_STATE"              : "",
-        "INFO_RADIUS_ADDL_REQUIRED"                   : "Please supply additional credentials"
+        "FIELD_HEADER_RADIUSCHALLENGE"                : ""
     }
 
 }
diff --git a/extensions/guacamole-auth-radius/src/main/resources/translations/ja.json b/extensions/guacamole-auth-radius/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..584f31b
--- /dev/null
+++ b/extensions/guacamole-auth-radius/src/main/resources/translations/ja.json
@@ -0,0 +1,7 @@
+{
+
+    "LOGIN" : {
+        "INFO_RADIUS_ADDL_REQUIRED"                   : "追加の認証情報を入力してください"
+    }
+
+}
diff --git a/extensions/guacamole-auth-totp/pom.xml b/extensions/guacamole-auth-totp/pom.xml
index 11199df..180cbde 100644
--- a/extensions/guacamole-auth-totp/pom.xml
+++ b/extensions/guacamole-auth-totp/pom.xml
@@ -220,6 +220,13 @@
             <version>1.1.0</version>
             <scope>provided</scope>
         </dependency>
+        
+        <!-- Guava - Utility Library -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.0.1-jre</version>
+        </dependency>
 
         <!-- Guice -->
         <dependency>
@@ -241,13 +248,6 @@
             <scope>provided</scope>
         </dependency>
 
-        <!-- Guava - Utility Library -->
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <version>18.0</version>
-        </dependency>
-
         <!-- JUnit -->
         <dependency>
             <groupId>junit</groupId>
diff --git a/extensions/guacamole-auth-totp/src/licenses/LICENSE b/extensions/guacamole-auth-totp/src/licenses/LICENSE
index 8a66d23..4607964 100644
--- a/extensions/guacamole-auth-totp/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-totp/src/licenses/LICENSE
@@ -232,10 +232,10 @@
 Guava: Google Core Libraries for Java (https://github.com/google/guava)
 -----------------------------------------------------------------------
 
-    Version: 18.0
+    Version: 27.0.1-jre
     From: 'Google Inc.' (http://www.google.com/)
     License(s):
-        Apache v2.0 (bundled/guava-18.0/COPYING)
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
 
 
 JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
diff --git a/extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING b/extensions/guacamole-auth-totp/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
rename from extensions/guacamole-auth-totp/src/licenses/bundled/guava-18.0/COPYING
rename to extensions/guacamole-auth-totp/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
index 1a61e89..58b6527 100644
--- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
+++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/form/AuthenticationCodeField.java
@@ -30,7 +30,6 @@
 import java.io.IOException;
 import java.net.URI;
 import javax.ws.rs.core.UriBuilder;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.auth.totp.user.UserTOTPKey;
 import org.apache.guacamole.auth.totp.conf.ConfigurationService;
@@ -309,7 +308,7 @@
 
         // Return data URI for generated image
         return "data:image/png;base64,"
-                + DatatypeConverter.printBase64Binary(stream.toByteArray());
+                + BaseEncoding.base64().encode(stream.toByteArray());
 
     }
 
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 31dda60..ed42d6f 100644
--- a/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-totp/src/main/resources/guac-manifest.json
@@ -10,7 +10,8 @@
     ],
 
     "translations" : [
-        "translations/en.json"
+        "translations/en.json",
+        "translations/ja.json"
     ],
 
     "js" : [
diff --git a/extensions/guacamole-auth-totp/src/main/resources/translations/ja.json b/extensions/guacamole-auth-totp/src/main/resources/translations/ja.json
new file mode 100644
index 0000000..a946ff4
--- /dev/null
+++ b/extensions/guacamole-auth-totp/src/main/resources/translations/ja.json
@@ -0,0 +1,26 @@
+{
+
+    "TOTP" : {
+
+        "ACTION_HIDE_DETAILS" : "非表示",
+        "ACTION_SHOW_DETAILS" : "表示",
+
+        "FIELD_HEADER_ALGORITHM"  : "アルゴリズム:",
+        "FIELD_HEADER_DIGITS"     : "認証コード桁数:",
+        "FIELD_HEADER_INTERVAL"   : "認証コード利用可能時間(秒):",
+        "FIELD_HEADER_SECRET_KEY" : "秘密鍵:",
+
+        "FIELD_PLACEHOLDER_CODE" : "認証コード",
+
+        "INFO_CODE_REQUIRED"       : "認証コードを入力してください。",
+        "INFO_ENROLL_REQUIRED"     : "二要素認証システムが有効になっています。",
+        "INFO_VERIFICATION_FAILED" : "認証に失敗しました。もう一度やり直してください。",
+
+        "HELP_ENROLL_BARCODE" : "スマートフォンやタブレット等のデバイスの二要素認証アプリでQRコードを読み込んでください。",
+        "HELP_ENROLL_VERIFY"  : "QRコードを読み込み、表示された {DIGITS}桁の認証コードを入力してください。",
+
+        "SECTION_HEADER_DETAILS" : "詳細:"
+
+    }
+
+}
diff --git a/guacamole-common-js/src/main/webapp/modules/Tunnel.js b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
index 4f056c9..149dce4 100644
--- a/guacamole-common-js/src/main/webapp/modules/Tunnel.js
+++ b/guacamole-common-js/src/main/webapp/modules/Tunnel.js
@@ -1015,7 +1015,7 @@
                     var opcode = elements.shift();
 
                     // Update state and UUID when first instruction received
-                    if (tunnel.state === Guacamole.Tunnel.State.CONNECTING) {
+                    if (tunnel.uuid === null) {
 
                         // Associate tunnel UUID if received
                         if (opcode === Guacamole.Tunnel.INTERNAL_DATA_OPCODE)
diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/URIGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/URIGuacamoleProperty.java
new file mode 100644
index 0000000..d40f91e
--- /dev/null
+++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/URIGuacamoleProperty.java
@@ -0,0 +1,45 @@
+/*
+ * 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.properties;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+
+/**
+ * A GuacamoleProperty whose value is a URI.
+ */
+public abstract class URIGuacamoleProperty implements GuacamoleProperty<URI> {
+    
+    @Override
+    public URI parseValue(String value) throws GuacamoleException {
+        
+        try {
+            return new URI(value);
+        }
+        catch (URISyntaxException e) {
+            throw new GuacamoleServerException("Value \"" + value
+                + "\" is not a valid URI.");
+        }
+        
+    }
+    
+}
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 3c9d4fe..932cd85 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
@@ -99,6 +99,7 @@
                         "failsafe",
                         "fr-fr-azerty",
                         "fr-ch-qwertz",
+                        "hu-hu-qwertz",                        
                         "it-it-qwerty",
                         "ja-jp-qwerty",
                         "pt-br-qwerty",
@@ -153,6 +154,22 @@
         },
 
         {
+            "name"  : "clipboard",
+            "fields" : [
+                {
+                    "name"    : "disable-copy",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                },
+                {
+                    "name"    : "disable-paste",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                }
+            ]
+        },
+
+        {
             "name"  : "device-redirection",
             "fields" : [
                 {
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 6a91279..a1d01ed 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
@@ -72,6 +72,22 @@
         },
 
         {
+            "name"  : "clipboard",
+            "fields" : [
+                {
+                    "name"    : "disable-copy",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                },
+                {
+                    "name"    : "disable-paste",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                }
+            ]
+        },
+
+        {
             "name" : "session",
             "fields" : [
                 {
@@ -92,6 +108,7 @@
                 }
             ]
         },
+
         {
             "name" : "behavior",
             "fields" : [
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
index 2526096..0f70f05 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/telnet.json
@@ -76,6 +76,22 @@
         },
 
         {
+            "name"  : "clipboard",
+            "fields" : [
+                {
+                    "name"    : "disable-copy",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                },
+                {
+                    "name"    : "disable-paste",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                }
+            ]
+        },
+
+        {
             "name" : "behavior",
             "fields" : [
                 {
diff --git a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/vnc.json b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/vnc.json
index bdeaebc..117053c 100644
--- a/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/vnc.json
+++ b/guacamole-ext/src/main/resources/org/apache/guacamole/protocols/vnc.json
@@ -20,6 +20,10 @@
             "name"  : "authentication",
             "fields" : [
                 {
+                    "name"  : "username",
+                    "type"  : "USERNAME"
+                },
+                {
                     "name"  : "password",
                     "type"  : "PASSWORD"
                 }
@@ -59,6 +63,16 @@
                     "name"    : "clipboard-encoding",
                     "type"    : "ENUM",
                     "options" : [ "", "ISO8859-1", "UTF-8", "UTF-16", "CP1252" ]
+                },
+                {
+                    "name"    : "disable-copy",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
+                },
+                {
+                    "name"    : "disable-paste",
+                    "type"    : "BOOLEAN",
+                    "options" : [ "true" ]
                 }
             ]
         },
diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index 8f90f27..d7cf465 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -257,7 +257,7 @@
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
-            <version>1.1.2</version>
+            <version>1.2.3</version>
         </dependency>
         
         <!-- Guacamole Java API -->
@@ -494,6 +494,13 @@
 
         </dependency>
         
+        <!-- Guava Base Libraries -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>27.0.1-jre</version>
+        </dependency>
+
         <!-- JSTZ for TimeZone Detection -->
         <dependency>
             <groupId>org.webjars.npm</groupId>
diff --git a/guacamole/src/licenses/LICENSE b/guacamole/src/licenses/LICENSE
index 7c696e5..2995811 100644
--- a/guacamole/src/licenses/LICENSE
+++ b/guacamole/src/licenses/LICENSE
@@ -529,6 +529,15 @@
 the terms above.
 
 
+Guava: Google Core Libraries for Java (https://github.com/google/guava)
+-----------------------------------------------------------------------
+
+    Version: 27.0.1-jre
+    From: 'Google Inc.' (http://www.google.com/)
+    License(s):
+        Apache v2.0 (bundled/guava-27.0.1-jre/COPYING)
+
+
 JSR-250 Reference Implementation
 (https://jcp.org/aboutJava/communityprocess/final/jsr250/index.html)
 --------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-18.0/COPYING b/guacamole/src/licenses/bundled/guava-27.0.1-jre/COPYING
similarity index 100%
copy from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-dist/src/licenses/bundled/guava-18.0/COPYING
copy to guacamole/src/licenses/bundled/guava-27.0.1-jre/COPYING
diff --git a/guacamole/src/licenses/bundled/jaxb-api-2.2.2/License.txt b/guacamole/src/licenses/bundled/jaxb-api-2.2.2/License.txt
deleted file mode 100644
index 159c16b..0000000
--- a/guacamole/src/licenses/bundled/jaxb-api-2.2.2/License.txt
+++ /dev/null
@@ -1,136 +0,0 @@
-COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1
-
-1. Definitions.
-
-     1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
-
-     1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
-
-     1.4. "Executable" means the Covered Software in any form other than Source Code.
-
-     1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License.
-
-     1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
-
-     1.7. "License" means this document.
-
-     1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means the Source Code and Executable form of any of the following:
-
-     A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
-
-     B. Any new file that contains any part of the Original Software or previous Modification; or
-
-     C. Any new file that is contributed or otherwise made available under the terms of this License.
-
-     1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License.
-
-     1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-     1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
-
-     1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. License Grants.
-
-     2.1. The Initial Developer Grant.
-
-     Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
-
-     (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
-
-     (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
-
-     (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
-
-     (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
-
-     2.2. Contributor Grant.
-
-     Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
-
-     (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
-
-     (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
-     (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
-
-     (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-     3.1. Availability of Source Code.
-
-     Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
-
-     3.2. Modifications.
-
-     The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
-
-     3.3. Required Notices.
-
-     You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
-
-     3.4. Application of Additional Terms.
-
-     You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-     3.5. Distribution of Executable Versions.
-
-     You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-     3.6. Larger Works.
-
-     You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
-
-4. Versions of the License.
-
-     4.1. New Versions.
-
-     Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
-
-     4.2. Effect of New Versions.
-
-     You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
-
-     4.3. Modified Versions.
-
-     When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
-
-5. DISCLAIMER OF WARRANTY.
-
-     COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-6. TERMINATION.
-
-     6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-     6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
-
-     6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-     6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
-
-7. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-8. U.S. GOVERNMENT END USERS.
-
-     The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
-
-9. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
-
-10. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-----------
-NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
-The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
-
diff --git a/guacamole/src/licenses/bundled/jaxb-impl-2.2.3-1/License.txt b/guacamole/src/licenses/bundled/jaxb-impl-2.2.3-1/License.txt
deleted file mode 100644
index 159c16b..0000000
--- a/guacamole/src/licenses/bundled/jaxb-impl-2.2.3-1/License.txt
+++ /dev/null
@@ -1,136 +0,0 @@
-COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1
-
-1. Definitions.
-
-     1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
-
-     1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
-
-     1.4. "Executable" means the Covered Software in any form other than Source Code.
-
-     1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License.
-
-     1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
-
-     1.7. "License" means this document.
-
-     1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means the Source Code and Executable form of any of the following:
-
-     A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
-
-     B. Any new file that contains any part of the Original Software or previous Modification; or
-
-     C. Any new file that is contributed or otherwise made available under the terms of this License.
-
-     1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License.
-
-     1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
-
-     1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
-
-     1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
-
-2. License Grants.
-
-     2.1. The Initial Developer Grant.
-
-     Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
-
-     (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
-
-     (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
-
-     (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
-
-     (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
-
-     2.2. Contributor Grant.
-
-     Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
-
-     (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
-
-     (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
-
-     (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
-
-     (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
-
-3. Distribution Obligations.
-
-     3.1. Availability of Source Code.
-
-     Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
-
-     3.2. Modifications.
-
-     The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
-
-     3.3. Required Notices.
-
-     You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
-
-     3.4. Application of Additional Terms.
-
-     You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
-
-     3.5. Distribution of Executable Versions.
-
-     You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
-
-     3.6. Larger Works.
-
-     You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
-
-4. Versions of the License.
-
-     4.1. New Versions.
-
-     Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
-
-     4.2. Effect of New Versions.
-
-     You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
-
-     4.3. Modified Versions.
-
-     When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
-
-5. DISCLAIMER OF WARRANTY.
-
-     COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-6. TERMINATION.
-
-     6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
-
-     6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
-
-     6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license.
-
-     6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
-
-7. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-8. U.S. GOVERNMENT END USERS.
-
-     The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
-
-9. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
-
-10. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
-
-----------
-NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
-The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
-
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java
index 7f38857..b168514 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/AuthenticationService.java
@@ -36,6 +36,7 @@
 import org.apache.guacamole.net.auth.UserContext;
 import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
 import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException;
+import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException;
 import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException;
 import org.apache.guacamole.net.event.AuthenticationFailureEvent;
 import org.apache.guacamole.net.event.AuthenticationSuccessEvent;
@@ -170,7 +171,13 @@
                     return authenticatedUser;
             }
 
-            // First failure takes priority for now
+            // Insufficient credentials should take precedence
+            catch (GuacamoleInsufficientCredentialsException e) {
+                if (authFailure == null || authFailure instanceof GuacamoleInvalidCredentialsException)
+                    authFailure = e;
+            }
+            
+            // Catch other credentials exceptions and assign the first one
             catch (GuacamoleCredentialsException e) {
                 if (authFailure == null)
                     authFailure = e;
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/SecureRandomAuthTokenGenerator.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/SecureRandomAuthTokenGenerator.java
index ac09ff0..09a6340 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/SecureRandomAuthTokenGenerator.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/SecureRandomAuthTokenGenerator.java
@@ -19,8 +19,8 @@
 
 package org.apache.guacamole.rest.auth;
 
+import com.google.common.io.BaseEncoding;
 import java.security.SecureRandom;
-import javax.xml.bind.DatatypeConverter;
 
 /**
  * An implementation of the AuthTokenGenerator based around SecureRandom.
@@ -37,7 +37,7 @@
         byte[] bytes = new byte[32];
         secureRandom.nextBytes(bytes);
         
-        return DatatypeConverter.printHexBinary(bytes);
+        return BaseEncoding.base16().encode(bytes);
     }
     
 }
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java
index e1ff66f..887b4f0 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/TokenRESTService.java
@@ -19,6 +19,7 @@
 
 package org.apache.guacamole.rest.auth;
 
+import com.google.common.io.BaseEncoding;
 import com.google.inject.Inject;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
@@ -33,7 +34,6 @@
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleResourceNotFoundException;
 import org.apache.guacamole.net.auth.AuthenticatedUser;
@@ -94,7 +94,8 @@
 
                     // Decode base64 authorization
                     String basicBase64 = authorization.substring(6);
-                    String basicCredentials = new String(DatatypeConverter.parseBase64Binary(basicBase64), "UTF-8");
+                    String basicCredentials = new String(
+                            BaseEncoding.base64().decode(basicBase64), "UTF-8");
 
                     // Pull username/password from auth data
                     int colon = basicCredentials.indexOf(':');
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/InputStreamInterceptingFilter.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/InputStreamInterceptingFilter.java
index 98c15c9..f8e0334 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/InputStreamInterceptingFilter.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/InputStreamInterceptingFilter.java
@@ -19,11 +19,11 @@
 
 package org.apache.guacamole.tunnel;
 
+import com.google.common.io.BaseEncoding;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.List;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.net.GuacamoleTunnel;
 import org.apache.guacamole.protocol.GuacamoleInstruction;
@@ -75,7 +75,7 @@
 
         // Send "blob" containing provided data
         sendInstruction(new GuacamoleInstruction("blob", index,
-            DatatypeConverter.printBase64Binary(blob)));
+           BaseEncoding.base64().encode(blob)));
 
     }
 
diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/OutputStreamInterceptingFilter.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/OutputStreamInterceptingFilter.java
index 85ae02c..99cf153 100644
--- a/guacamole/src/main/java/org/apache/guacamole/tunnel/OutputStreamInterceptingFilter.java
+++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/OutputStreamInterceptingFilter.java
@@ -19,10 +19,10 @@
 
 package org.apache.guacamole.tunnel;
 
+import com.google.common.io.BaseEncoding;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.List;
-import javax.xml.bind.DatatypeConverter;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.net.GuacamoleTunnel;
 import org.apache.guacamole.protocol.GuacamoleInstruction;
@@ -127,7 +127,7 @@
         byte[] blob;
         try {
             String data = args.get(1);
-            blob = DatatypeConverter.parseBase64Binary(data);
+            blob = BaseEncoding.base64().decode(data);
         }
         catch (IllegalArgumentException e) {
             logger.warn("Received base64 data for intercepted stream was invalid.");
diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index b90f263..c8941b3 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -24,6 +24,7 @@
         function clientController($scope, $routeParams, $injector) {
 
     // Required types
+    var ConnectionGroup    = $injector.get('ConnectionGroup');
     var ManagedClient      = $injector.get('ManagedClient');
     var ManagedClientState = $injector.get('ManagedClientState');
     var ManagedFilesystem  = $injector.get('ManagedFilesystem');
@@ -31,16 +32,18 @@
     var ScrollState        = $injector.get('ScrollState');
 
     // Required services
-    var $location             = $injector.get('$location');
-    var authenticationService = $injector.get('authenticationService');
-    var clipboardService      = $injector.get('clipboardService');
-    var guacClientManager     = $injector.get('guacClientManager');
-    var guacNotification      = $injector.get('guacNotification');
-    var iconService           = $injector.get('iconService');
-    var preferenceService     = $injector.get('preferenceService');
-    var requestService        = $injector.get('requestService');
-    var tunnelService         = $injector.get('tunnelService');
-    var userPageService       = $injector.get('userPageService');
+    var $location              = $injector.get('$location');
+    var authenticationService  = $injector.get('authenticationService');
+    var connectionGroupService = $injector.get('connectionGroupService');
+    var clipboardService       = $injector.get('clipboardService');
+    var dataSourceService      = $injector.get('dataSourceService');
+    var guacClientManager      = $injector.get('guacClientManager');
+    var guacNotification       = $injector.get('guacNotification');
+    var iconService            = $injector.get('iconService');
+    var preferenceService      = $injector.get('preferenceService');
+    var requestService         = $injector.get('requestService');
+    var tunnelService          = $injector.get('tunnelService');
+    var userPageService        = $injector.get('userPageService');
 
     /**
      * The minimum number of pixels a drag gesture must move to result in the
@@ -284,6 +287,64 @@
     $scope.client = guacClientManager.getManagedClient($routeParams.id, $routeParams.params);
 
     /**
+     * All active clients which are not the current client ($scope.client).
+     * Each key is the ID of the connection used by that client.
+     *
+     * @type Object.<String, ManagedClient>
+     */
+    $scope.otherClients = (function getOtherClients(clients) {
+        var otherClients = angular.extend({}, clients);
+        delete otherClients[$scope.client.id];
+        return otherClients;
+    })(guacClientManager.getManagedClients());
+
+    /**
+     * The root connection groups of the connection hierarchy that should be
+     * presented to the user for selecting a different connection, as a map of
+     * data source identifier to the root connection group of that data
+     * source. This will be null if the connection group hierarchy has not yet
+     * been loaded or if the hierarchy is inapplicable due to only one
+     * connection or balancing group being available.
+     *
+     * @type Object.<String, ConnectionGroup>
+     */
+    $scope.rootConnectionGroups = null;
+
+    /**
+     * Array of all connection properties that are filterable.
+     *
+     * @type String[]
+     */
+    $scope.filteredConnectionProperties = [
+        'name'
+    ];
+
+    /**
+     * Array of all connection group properties that are filterable.
+     *
+     * @type String[]
+     */
+    $scope.filteredConnectionGroupProperties = [
+        'name'
+    ];
+
+    // Retrieve root groups and all descendants
+    dataSourceService.apply(
+        connectionGroupService.getConnectionGroupTree,
+        authenticationService.getAvailableDataSources(),
+        ConnectionGroup.ROOT_IDENTIFIER
+    )
+    .then(function rootGroupsRetrieved(rootConnectionGroups) {
+
+        // Store retrieved groups only if there are multiple connections or
+        // balancing groups available
+        var clientPages = userPageService.getClientPages(rootConnectionGroups);
+        if (clientPages.length > 1)
+            $scope.rootConnectionGroups = rootConnectionGroups;
+
+    }, requestService.WARN);
+
+    /**
      * Map of all available sharing profiles for the current connection by
      * their identifiers. If this information is not yet available, or no such
      * sharing profiles exist, this will be an empty object.
@@ -467,6 +528,12 @@
 
     });
 
+    // Update last used timestamp when the active client changes
+    $scope.$watch('client', function clientChanged(client) {
+        if (client)
+            client.lastUsed = new Date().getTime();
+    });
+
     // Update page icon when thumbnail changes
     $scope.$watch('client.thumbnail.canvas', function thumbnailChanged(canvas) {
         iconService.setIcons(canvas);
diff --git a/guacamole/src/main/webapp/app/client/directives/guacClient.js b/guacamole/src/main/webapp/app/client/directives/guacClient.js
index 769edd7..cc88296 100644
--- a/guacamole/src/main/webapp/app/client/directives/guacClient.js
+++ b/guacamole/src/main/webapp/app/client/directives/guacClient.js
@@ -282,6 +282,9 @@
                     return false;
                 };
 
+                // Size of newly-attached client may be different
+                $scope.mainElementResized();
+
             });
 
             // Update actual view scrollLeft when scroll properties change
diff --git a/guacamole/src/main/webapp/app/client/directives/guacClientPanel.js b/guacamole/src/main/webapp/app/client/directives/guacClientPanel.js
new file mode 100644
index 0000000..658be2f
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/directives/guacClientPanel.js
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+/**
+ * A toolbar/panel which displays a list of active Guacamole connections. The
+ * panel is fixed to the bottom-right corner of its container and can be
+ * manually hidden/exposed by the user.
+ */
+angular.module('client').directive('guacClientPanel', ['$injector', function guacClientPanel($injector) {
+
+    // Required services
+    var guacClientManager     = $injector.get('guacClientManager');
+    var sessionStorageFactory = $injector.get('sessionStorageFactory');
+
+    // Required types
+    var ManagedClientState = $injector.get('ManagedClientState');
+
+    /**
+     * Getter/setter for the boolean flag controlling whether the client panel
+     * is currently hidden. This flag is maintained in session-local storage to
+     * allow the state of the panel to persist despite navigation within the
+     * same tab. When hidden, the panel will be collapsed against the right
+     * side of the container. By default, the panel is visible.
+     *
+     * @type Function
+     */
+    var panelHidden = sessionStorageFactory.create(false);
+
+    return {
+        // Element only
+        restrict: 'E',
+        replace: true,
+        scope: {
+
+            /**
+             * The ManagedClient instances associated with the active
+             * connections to be displayed within this panel.
+             * 
+             * @type ManagedClient[]|Object.<String, ManagedClient>
+             */
+            clients : '='
+
+        },
+        templateUrl: 'app/client/templates/guacClientPanel.html',
+        controller: ['$scope', '$element', function guacClientPanelController($scope, $element) {
+
+            /**
+             * The DOM element containing the scrollable portion of the client
+             * panel.
+             *
+             * @type Element
+             */
+            var scrollableArea = $element.find('.client-panel-connection-list')[0];
+
+            /**
+             * On-scope reference to session-local storage of the flag
+             * controlling whether then panel is hidden.
+             */
+            $scope.panelHidden = panelHidden;
+
+            /**
+             * Returns whether this panel currently has any clients associated
+             * with it.
+             *
+             * @return {Boolean}
+             *     true if at least one client is associated with this panel,
+             *     false otherwise.
+             */
+            $scope.hasClients = function hasClients() {
+                return !!_.find($scope.clients, $scope.isManaged);
+            };
+
+            /**
+             * Returns whether the status of the given client has changed in a
+             * way that requires the user's attention. This may be due to an
+             * error, or due to a server-initiated disconnect.
+             *
+             * @param {ManagedClient} client
+             *     The client to test.
+             *
+             * @returns {Boolean}
+             *     true if the given client requires the user's attention,
+             *     false otherwise.
+             */
+            $scope.hasStatusUpdate = function hasStatusUpdate(client) {
+
+                // Test whether the client has encountered an error
+                switch (client.clientState.connectionState) {
+                    case ManagedClientState.ConnectionState.CONNECTION_ERROR:
+                    case ManagedClientState.ConnectionState.TUNNEL_ERROR:
+                    case ManagedClientState.ConnectionState.DISCONNECTED:
+                        return true;
+                }
+
+                return false;
+
+            };
+
+            /**
+             * Returns whether the given client is currently being managed by
+             * the guacClientManager service.
+             *
+             * @param {ManagedClient} client
+             *     The client to test.
+             *
+             * @returns {Boolean}
+             *     true if the given client is being managed by the
+             *     guacClientManager service, false otherwise.
+             */
+            $scope.isManaged = function isManaged(client) {
+                return !!guacClientManager.getManagedClients()[client.id];
+            };
+
+            /**
+             * Initiates an orderly disconnect of the given client. The client
+             * is removed from management such that attempting to connect to
+             * the same connection will result in a new connection being
+             * established, rather than displaying a notification that the
+             * connection has ended.
+             *
+             * @param {type} client
+             * @returns {undefined}
+             */
+            $scope.disconnect = function disconnect(client) {
+                client.client.disconnect();
+                guacClientManager.removeManagedClient(client.id);
+            };
+
+            /**
+             * Toggles whether the client panel is currently hidden.
+             */
+            $scope.togglePanel = function togglePanel() {
+                panelHidden(!panelHidden());
+            };
+
+            // Override vertical scrolling, scrolling horizontally instead
+            scrollableArea.addEventListener('wheel', function reorientVerticalScroll(e) {
+
+                var deltaMultiplier = {
+                    /* DOM_DELTA_PIXEL */ 0x00: 1,
+                    /* DOM_DELTA_LINE  */ 0x01: 15,
+                    /* DOM_DELTA_PAGE  */ 0x02: scrollableArea.offsetWidth
+                };
+
+                if (e.deltaY) {
+                    this.scrollLeft += e.deltaY * (deltaMultiplier[e.deltaMode] || deltaMultiplier(0x01));
+                    e.preventDefault();
+                }
+
+            });
+
+        }]
+    };
+}]);
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/client/styles/connection-select-menu.css b/guacamole/src/main/webapp/app/client/styles/connection-select-menu.css
new file mode 100644
index 0000000..3abfaa4
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/styles/connection-select-menu.css
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#guac-menu .header h2.connection-select-menu {
+    overflow: visible;
+}
+
+.connection-select-menu {
+    padding: 0;
+    min-width: 0;
+}
+
+.connection-select-menu .menu-dropdown {
+    border: none;
+}
+
+.connection-select-menu .menu-dropdown .menu-contents {
+    font-weight: normal;
+    font-size: 0.8em;
+    right: auto;
+    left: 0;
+    max-width: 100vw;
+    width: 400px;
+}
+
+.connection-select-menu .menu-dropdown .menu-contents .filter input {
+    border-bottom: 1px solid rgba(0,0,0,0.125);
+    border-left: none;
+}
+
+.connection-select-menu .menu-dropdown .menu-contents .filter {
+    margin-bottom: 0.5em;
+    padding: 0;
+}
+
+.connection-select-menu .menu-dropdown .menu-contents .group-list .caption {
+    display: inline-block;
+    width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
diff --git a/guacamole/src/main/webapp/app/client/styles/guac-menu.css b/guacamole/src/main/webapp/app/client/styles/guac-menu.css
index 9e0fb7c..aa80e09 100644
--- a/guacamole/src/main/webapp/app/client/styles/guac-menu.css
+++ b/guacamole/src/main/webapp/app/client/styles/guac-menu.css
@@ -65,6 +65,13 @@
     margin-top: 1em;
 }
 
+#guac-menu .header h2 {
+    white-space: nowrap;
+    overflow: hidden;
+    width: 100%;
+    text-overflow: ellipsis;
+}
+
 #guac-menu #mouse-settings .choice {
     text-align: center;
 }
diff --git a/guacamole/src/main/webapp/app/client/styles/keyboard.css b/guacamole/src/main/webapp/app/client/styles/keyboard.css
index 8076d54..e5bb963 100644
--- a/guacamole/src/main/webapp/app/client/styles/keyboard.css
+++ b/guacamole/src/main/webapp/app/client/styles/keyboard.css
@@ -18,6 +18,8 @@
  */
 
 .keyboard-container {
+
+    display: none;
     text-align: center;
 
     width: 100%;
@@ -29,4 +31,9 @@
     opacity: 0.85;
 
     z-index: 1;
+
 }
+
+.keyboard-container.open {
+    display: block;
+}
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/client/styles/other-connections.css b/guacamole/src/main/webapp/app/client/styles/other-connections.css
new file mode 100644
index 0000000..6c57aaa
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/styles/other-connections.css
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+#other-connections .client-panel {
+
+    display: none;
+    position: absolute;
+    right: 0;
+    bottom: 0;
+
+    border: 1px solid rgba(255, 255, 255, 0.25);
+    background: rgba(0, 0, 0, 0.25);
+    max-width: 100%;
+    white-space: nowrap;
+    transition: max-width 0.125s, width 0.125s;
+
+    /* Render above modal status */
+    z-index: 20;
+
+}
+
+#other-connections .client-panel.has-clients {
+    display: block;
+}
+
+#other-connections .client-panel.hidden {
+    max-width: 16px;
+}
+
+#other-connections .client-panel-handle {
+
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    height: 100%;
+    width: 16px;
+    z-index: 1;
+
+    background-color: white;
+    background-repeat: no-repeat;
+    background-size: contain;
+    background-position: center center;
+    background-image: url(images/arrows/right.png);
+    opacity: 0.5;
+
+}
+
+#other-connections .client-panel-handle:hover {
+    opacity: 0.75;
+}
+
+#other-connections .client-panel.hidden .client-panel-handle {
+    background-image: url(images/arrows/left.png);
+}
+
+#other-connections .client-panel-connection-list {
+
+    text-align: right;
+
+    margin: 0;
+    padding: 0;
+    padding-left: 16px;
+
+    overflow-x: auto;
+    overflow-y: hidden;
+
+}
+
+#other-connections .client-panel-connection {
+
+    display: inline-block;
+    position: relative;
+
+    margin: 0.5em;
+    border: 1px solid white;
+    background: black;
+    box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
+
+    opacity: 0.5;
+    transition: opacity 0.25s;
+
+    max-height: 128px;
+    overflow: hidden;
+    vertical-align: middle;
+
+}
+
+#other-connections .client-panel-connection .thumbnail-main img {
+    max-width: none;
+    max-height: 128px;
+}
+
+#other-connections .client-panel-connection a[href]::before {
+
+    display: block;
+    content: ' ';
+
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 100%;
+    width: 100%;
+    z-index: 1;
+
+    background: url('images/warning-white.png');
+    background-size: 48px;
+    background-position: center;
+    background-repeat: no-repeat;
+    background-color: black;
+
+    opacity: 0;
+    transition: opacity 0.25s;
+
+}
+
+#other-connections .client-panel-connection.needs-attention a[href]::before {
+    opacity: 0.75;
+}
+
+#other-connections button.close-other-connection {
+
+    position: absolute;
+    top: 0;
+    right: 0;
+    z-index: 2;
+
+    margin: 0;
+    padding: 4px;
+    min-width: 0;
+    border: none;
+    background: transparent;
+    box-shadow: none;
+    text-shadow: none;
+
+    opacity: 0.5;
+    line-height: 1;
+
+}
+
+#other-connections button.close-other-connection:hover {
+    opacity: 1;
+}
+
+#other-connections button.close-other-connection img {
+    background: #A43;
+    border-radius: 18px;
+    max-width: 18px;
+    padding: 3px;
+}
+
+#other-connections button.close-other-connection:hover img {
+    background: #C54;
+}
+
+#other-connections .client-panel.hidden .client-panel-connection-list {
+    /* Hide scrollbar when panel is hidden (will be visible through panel
+     * show/hide button otherwise) */
+    overflow-x: hidden;
+}
+
+#other-connections .client-panel.hidden .client-panel-connection {
+    /* Hide thumbnails when panel is hidden (will be visible through panel
+     * show/hide button otherwise) */
+    visibility: hidden;
+}
+
+#other-connections .client-panel-connection .name {
+
+    position: absolute;
+    padding: 0.25em 0.5em;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 2;
+
+    text-align: left;
+    color: white;
+    background: rgba(0, 0, 0, 0.5);
+    font-size: 0.75em;
+    font-weight: bold;
+
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+
+}
+
+#other-connections .client-panel-connection:hover {
+    opacity: 1;
+}
diff --git a/guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js b/guacamole/src/main/webapp/app/client/styles/text-input.css
similarity index 74%
rename from guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js
rename to guacamole/src/main/webapp/app/client/styles/text-input.css
index 114d598..28e905e 100644
--- a/guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js
+++ b/guacamole/src/main/webapp/app/client/styles/text-input.css
@@ -17,15 +17,10 @@
  * under the License.
  */
 
-/**
- * The config block for setting up the HTTP PATCH method.
- */
-angular.module('index').config(['$httpProvider', 
-        function indexHttpPatchConfig($httpProvider) {
-    
-    $httpProvider.defaults.headers.patch = {
-        'Content-Type': 'application/json'
-    }
-}]);
+.text-input-container {
+    display: none;
+}
 
-
+.text-input-container.open {
+    display: block;
+}
diff --git a/guacamole/src/main/webapp/app/client/templates/client.html b/guacamole/src/main/webapp/app/client/templates/client.html
index 7690e75..5325b47 100644
--- a/guacamole/src/main/webapp/app/client/templates/client.html
+++ b/guacamole/src/main/webapp/app/client/templates/client.html
@@ -8,21 +8,26 @@
             <!-- Central portion of view -->
             <div class="client-body" guac-touch-drag="clientDrag" guac-touch-pinch="clientPinch">
 
-                <!-- Client -->
+                <!-- Client for current connection -->
                 <guac-client client="client"></guac-client>
 
+                <!-- All other active connections -->
+                <div id="other-connections">
+                    <guac-client-panel clients="otherClients"></guac-client-panel>
+                </div>
+
             </div>
 
             <!-- Bottom portion of view -->
             <div class="client-bottom">
 
                 <!-- Text input -->
-                <div class="text-input-container" ng-show="showTextInput">
+                <div class="text-input-container" ng-class="{ open : showTextInput }">
                     <guac-text-input needs-focus="showTextInput"></guac-text-input>
                 </div>
 
                 <!-- On-screen keyboard -->
-                <div class="keyboard-container" ng-show="showOSK">
+                <div class="keyboard-container" ng-class="{ open : showOSK }">
                     <guac-osk layout="'CLIENT.URL_OSK_LAYOUT' | translate"></guac-osk>
                 </div>
 
@@ -47,7 +52,25 @@
 
             <!-- Stationary header -->
             <div class="header">
-                <h2>{{client.name}}</h2>
+                <h2 ng-hide="rootConnectionGroups">{{client.name}}</h2>
+                <h2 class="connection-select-menu" ng-show="rootConnectionGroups">
+                    <guac-menu menu-title="client.name" interactive="true">
+                        <div class="all-connections">
+                            <guac-group-list-filter connection-groups="rootConnectionGroups"
+                                filtered-connection-groups="filteredRootConnectionGroups"
+                                placeholder="'CLIENT.FIELD_PLACEHOLDER_FILTER' | translate"
+                                connection-properties="filteredConnectionProperties"
+                                connection-group-properties="filteredConnectionGroupProperties"></guac-group-list-filter>
+                            <guac-group-list
+                                connection-groups="filteredRootConnectionGroups"
+                                templates="{
+                                    'connection'       : 'app/client/templates/connection.html',
+                                    'connection-group' : 'app/client/templates/connectionGroup.html'
+                                }"
+                                page-size="10"></guac-group-list>
+                        </div>
+                    </guac-menu>
+                </h2>
                 <div class="share-menu" ng-show="canShareConnection()">
                     <guac-menu menu-title="'CLIENT.ACTION_SHARE' | translate">
                         <ul ng-repeat="sharingProfile in sharingProfiles">
diff --git a/guacamole/src/main/webapp/app/client/templates/connection.html b/guacamole/src/main/webapp/app/client/templates/connection.html
new file mode 100644
index 0000000..f6c1e70
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/templates/connection.html
@@ -0,0 +1,4 @@
+<a class="connection" ng-href="#/client/{{ item.getClientIdentifier() }}">
+    <div class="icon type" ng-class="item.protocol"></div>
+    <span class="name">{{item.name}}</span>
+</a>
diff --git a/guacamole/src/main/webapp/app/client/templates/connectionGroup.html b/guacamole/src/main/webapp/app/client/templates/connectionGroup.html
new file mode 100644
index 0000000..b2ea62c
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/templates/connectionGroup.html
@@ -0,0 +1,4 @@
+<span class="connection-group name">
+    <a ng-show="item.balancing" ng-href="#/client/{{ item.getClientIdentifier() }}">{{item.name}}</a>
+    <span ng-show="!item.balancing">{{item.name}}</span>
+</span>
diff --git a/guacamole/src/main/webapp/app/client/templates/guacClientPanel.html b/guacamole/src/main/webapp/app/client/templates/guacClientPanel.html
new file mode 100644
index 0000000..bb6e7e3
--- /dev/null
+++ b/guacamole/src/main/webapp/app/client/templates/guacClientPanel.html
@@ -0,0 +1,32 @@
+<div class="client-panel"
+     ng-class="{ 'has-clients': hasClients(), 'hidden' : panelHidden() }">
+
+    <!-- Toggle panel visibility -->
+    <div class="client-panel-handle" ng-click="togglePanel()"></div>
+
+    <!-- List of connection thumbnails -->
+    <ul class="client-panel-connection-list">
+        <li ng-repeat="client in clients | toArray | orderBy: [ '-value.lastUsed', 'value.title' ]"
+            ng-class="{ 'needs-attention' : hasStatusUpdate(client.value) }"
+            ng-show="isManaged(client.value)"
+            class="client-panel-connection">
+
+            <!-- Close connection -->
+            <button class="close-other-connection" ng-click="disconnect(client.value)">
+                <img ng-attr-alt="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}"
+                     ng-attr-title="{{ 'CLIENT.ACTION_DISCONNECT' | translate }}"
+                     src="images/x.png">
+            </button>
+
+            <!-- Thumbnail -->
+            <a href="#/client/{{client.value.id}}">
+                <div class="thumbnail">
+                    <guac-thumbnail client="client.value"></guac-thumbnail>
+                </div>
+                <div class="name">{{ client.value.title }}</div>
+            </a>
+
+        </li>
+    </ul>
+
+</div>
\ No newline at end of file
diff --git a/guacamole/src/main/webapp/app/client/types/ManagedClient.js b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
index 800eaf4..c1eccdd 100644
--- a/guacamole/src/main/webapp/app/client/types/ManagedClient.js
+++ b/guacamole/src/main/webapp/app/client/types/ManagedClient.js
@@ -84,6 +84,16 @@
         this.id = template.id;
 
         /**
+         * The time that the connection was last brought to the foreground of
+         * the current tab, as the number of milliseconds elapsed since
+         * midnight of January 1, 1970 UTC. If the connection has not yet been
+         * viewed, this will be 0.
+         *
+         * @type Number
+         */
+        this.lastUsed = template.lastUsed || 0;
+
+        /**
          * The actual underlying Guacamole client.
          *
          * @type Guacamole.Client
diff --git a/guacamole/src/main/webapp/app/element/directives/guacFocus.js b/guacamole/src/main/webapp/app/element/directives/guacFocus.js
index ce6093c..5087e7f 100644
--- a/guacamole/src/main/webapp/app/element/directives/guacFocus.js
+++ b/guacamole/src/main/webapp/app/element/directives/guacFocus.js
@@ -20,7 +20,11 @@
 /**
  * A directive which allows elements to be manually focused / blurred.
  */
-angular.module('element').directive('guacFocus', ['$parse', function guacFocus($parse) {
+angular.module('element').directive('guacFocus', ['$injector', function guacFocus($injector) {
+
+    // Required services
+    var $parse   = $injector.get('$parse');
+    var $timeout = $injector.get('$timeout');
 
     return {
         restrict: 'A',
@@ -44,7 +48,7 @@
 
             // Set/unset focus depending on value of guacFocus
             $scope.$watch(guacFocus, function updateFocus(value) {
-                $scope.$evalAsync(function updateFocusAsync() {
+                $timeout(function updateFocusAfterRender() {
                     if (value)
                         element.focus();
                     else
@@ -52,20 +56,6 @@
                 });
             });
 
-            // Set focus flag when focus is received
-            element.addEventListener('focus', function focusReceived() {
-                $scope.$evalAsync(function setGuacFocusAsync() {
-                    guacFocus.assign($scope, true);
-                });
-            });
-
-            // Unset focus flag when focus is lost
-            element.addEventListener('blur', function focusLost() {
-                $scope.$evalAsync(function unsetGuacFocusAsync() {
-                    guacFocus.assign($scope, false);
-                });
-            });
-
         } // end guacFocus link function
 
     };
diff --git a/guacamole/src/main/webapp/app/form/directives/form.js b/guacamole/src/main/webapp/app/form/directives/form.js
index 518db7d..81f500f 100644
--- a/guacamole/src/main/webapp/app/form/directives/form.js
+++ b/guacamole/src/main/webapp/app/form/directives/form.js
@@ -64,7 +64,22 @@
              *
              * @type Boolean
              */
-            modelOnly : '='
+            modelOnly : '=',
+
+            /**
+             * Whether the contents of the form should be rendered as disabled.
+             * By default, form fields are enabled.
+             *
+             * @type Boolean
+             */
+            disabled : '=',
+
+            /**
+             * The name of the field to be focused, if any.
+             *
+             * @type String
+             */
+            focused : '='
 
         },
         templateUrl: 'app/form/templates/form.html',
@@ -173,6 +188,19 @@
             });
 
             /**
+             * Returns whether the given field should be focused or not.
+             *
+             * @param {Field} field
+             *     The field to check.
+             *
+             * @returns {Boolean}
+             *     true if the given field should be focused, false otherwise.
+             */
+            $scope.isFocused = function isFocused(field) {
+                return field && (field.name === $scope.focused);
+            };
+
+            /**
              * Returns whether the given field should be displayed to the
              * current user.
              *
diff --git a/guacamole/src/main/webapp/app/form/directives/formField.js b/guacamole/src/main/webapp/app/form/directives/formField.js
index 41db0c1..fbc0cfe 100644
--- a/guacamole/src/main/webapp/app/form/directives/formField.js
+++ b/guacamole/src/main/webapp/app/form/directives/formField.js
@@ -53,7 +53,22 @@
              *
              * @type String
              */
-            model : '='
+            model : '=',
+
+            /**
+             * Whether this field should be rendered as disabled. By default,
+             * form fields are enabled.
+             *
+             * @type Boolean
+             */
+            disabled : '=',
+
+            /**
+             * Whether this field should be focused.
+             *
+             * @type Boolean
+             */
+            focused : '='
 
         },
         templateUrl: 'app/form/templates/formField.html',
diff --git a/guacamole/src/main/webapp/app/form/services/formService.js b/guacamole/src/main/webapp/app/form/services/formService.js
index 4198c10..1df49dc 100644
--- a/guacamole/src/main/webapp/app/form/services/formService.js
+++ b/guacamole/src/main/webapp/app/form/services/formService.js
@@ -281,6 +281,10 @@
          * model:
          *     The current String value of the field, if any.
          *
+         * disabled:
+         *     A boolean value which is true if the field should be disabled.
+         *     If false or undefined, the field should be enabled.
+         *
          * @param {Element} fieldContainer
          *     The DOM Element whose contents should be replaced with the
          *     compiled field template.
diff --git a/guacamole/src/main/webapp/app/form/templates/checkboxField.html b/guacamole/src/main/webapp/app/form/templates/checkboxField.html
index 110ab14..e906f7d 100644
--- a/guacamole/src/main/webapp/app/form/templates/checkboxField.html
+++ b/guacamole/src/main/webapp/app/form/templates/checkboxField.html
@@ -1,5 +1,7 @@
 <input type="checkbox"
        ng-attr-id="{{ fieldId }}"
+       ng-disabled="disabled"
        ng-model="typedValue"
+       guac-focus="focused"
        autocorrect="off"
        autocapitalize="off"/>
diff --git a/guacamole/src/main/webapp/app/form/templates/dateField.html b/guacamole/src/main/webapp/app/form/templates/dateField.html
index 2da96a1..7673e36 100644
--- a/guacamole/src/main/webapp/app/form/templates/dateField.html
+++ b/guacamole/src/main/webapp/app/form/templates/dateField.html
@@ -1,9 +1,11 @@
 <div class="date-field">
     <input type="date"
+           ng-disabled="disabled"
            ng-attr-id="{{ fieldId }}"
            ng-model="typedValue"
            ng-model-options="modelOptions"
            guac-lenient-date
+           guac-focus="focused"
            placeholder="{{'FORM.FIELD_PLACEHOLDER_DATE' | translate}}"
            autocorrect="off"
            autocapitalize="off"/>
diff --git a/guacamole/src/main/webapp/app/form/templates/emailField.html b/guacamole/src/main/webapp/app/form/templates/emailField.html
index 4ef14f4..cbfbb90 100644
--- a/guacamole/src/main/webapp/app/form/templates/emailField.html
+++ b/guacamole/src/main/webapp/app/form/templates/emailField.html
@@ -1,8 +1,10 @@
 <div class="email-field">
     <input type="email"
+           ng-disabled="disabled"
            ng-attr-id="{{ fieldId }}"
            ng-model="model"
            ng-hide="readOnly"
+           guac-focus="focused"
            autocorrect="off"
            autocapitalize="off"/>
     <a href="mailto:{{model}}" ng-show="readOnly">{{model}}</a>
diff --git a/guacamole/src/main/webapp/app/form/templates/form.html b/guacamole/src/main/webapp/app/form/templates/form.html
index b58393f..6b19bcc 100644
--- a/guacamole/src/main/webapp/app/form/templates/form.html
+++ b/guacamole/src/main/webapp/app/form/templates/form.html
@@ -9,7 +9,10 @@
         <div class="fields">
             <guac-form-field ng-repeat="field in form.fields" namespace="namespace"
                              ng-if="isVisible(field)"
-                             field="field" model="values[field.name]"></guac-form-field>
+                             data-disabled="disabled"
+                             focused="isFocused(field)"
+                             field="field"
+                             model="values[field.name]"></guac-form-field>
         </div>
 
     </div>
diff --git a/guacamole/src/main/webapp/app/form/templates/languageField.html b/guacamole/src/main/webapp/app/form/templates/languageField.html
index b1f25e8..2a22ff2 100644
--- a/guacamole/src/main/webapp/app/form/templates/languageField.html
+++ b/guacamole/src/main/webapp/app/form/templates/languageField.html
@@ -1,3 +1,4 @@
-<select ng-attr-id="{{ fieldId }}"
+<select guac-focus="focused"
+        ng-attr-id="{{ fieldId }}"
         ng-model="model"
         ng-options="language.key as language.value for language in languages | toArray | orderBy: key"></select>
diff --git a/guacamole/src/main/webapp/app/form/templates/numberField.html b/guacamole/src/main/webapp/app/form/templates/numberField.html
index f802b4a..c86fb8e 100644
--- a/guacamole/src/main/webapp/app/form/templates/numberField.html
+++ b/guacamole/src/main/webapp/app/form/templates/numberField.html
@@ -1,5 +1,7 @@
 <input type="number"
+       ng-disabled="disabled"
        ng-attr-id="{{ fieldId }}"
        ng-model="typedValue"
+       guac-focus="focused"
        autocorrect="off"
        autocapitalize="off"/>
diff --git a/guacamole/src/main/webapp/app/form/templates/passwordField.html b/guacamole/src/main/webapp/app/form/templates/passwordField.html
index 69d67e8..35eba9e 100644
--- a/guacamole/src/main/webapp/app/form/templates/passwordField.html
+++ b/guacamole/src/main/webapp/app/form/templates/passwordField.html
@@ -1,8 +1,10 @@
 <div class="password-field">
     <input type="{{passwordInputType}}"
+           ng-disabled="disabled"
            ng-attr-id="{{ fieldId }}"
            ng-model="model"
            ng-trim="false"
+           guac-focus="focused"
            autocorrect="off"
            autocapitalize="off"/>
     <div class="icon toggle-password" ng-click="togglePassword()" title="{{getTogglePasswordHelpText() | translate}}"></div>
diff --git a/guacamole/src/main/webapp/app/form/templates/selectField.html b/guacamole/src/main/webapp/app/form/templates/selectField.html
index e5ce0e9..2c672aa 100644
--- a/guacamole/src/main/webapp/app/form/templates/selectField.html
+++ b/guacamole/src/main/webapp/app/form/templates/selectField.html
@@ -1,3 +1,5 @@
 <select ng-attr-id="{{ fieldId }}"
+        ng-disabled="disabled"
+        guac-focus="focused"
         ng-model="model"
         ng-options="option as getFieldOption(option) | translate for option in field.options | orderBy: value"></select>
diff --git a/guacamole/src/main/webapp/app/form/templates/textAreaField.html b/guacamole/src/main/webapp/app/form/templates/textAreaField.html
index 2d4144b..9761af7 100644
--- a/guacamole/src/main/webapp/app/form/templates/textAreaField.html
+++ b/guacamole/src/main/webapp/app/form/templates/textAreaField.html
@@ -1,4 +1,6 @@
 <textarea ng-attr-id="{{ fieldId }}"
           ng-model="model"
+          ng-disabled="disabled"
+          guac-focus="focused"
           autocorrect="off"
           autocapitalize="off"></textarea>
diff --git a/guacamole/src/main/webapp/app/form/templates/textField.html b/guacamole/src/main/webapp/app/form/templates/textField.html
index 938b81c..3aea2bc 100644
--- a/guacamole/src/main/webapp/app/form/templates/textField.html
+++ b/guacamole/src/main/webapp/app/form/templates/textField.html
@@ -3,6 +3,8 @@
            ng-attr-id="{{ fieldId }}"
            ng-attr-list="{{ dataListId }}"
            ng-model="model"
+           ng-disabled="disabled"
+           guac-focus="focused"
            autocorrect="off"
            autocapitalize="off"/>
     <datalist ng-if="dataListId" ng-attr-id="{{ dataListId }}">
diff --git a/guacamole/src/main/webapp/app/form/templates/timeField.html b/guacamole/src/main/webapp/app/form/templates/timeField.html
index 292e44c..2a88230 100644
--- a/guacamole/src/main/webapp/app/form/templates/timeField.html
+++ b/guacamole/src/main/webapp/app/form/templates/timeField.html
@@ -1,8 +1,10 @@
 <div class="time-field">
     <input type="time"
+           ng-disabled="disabled"
            ng-attr-id="{{ fieldId }}"
            ng-model="typedValue"
            ng-model-options="modelOptions"
+           guac-focus="focused"
            guac-lenient-time
            placeholder="{{'FORM.FIELD_PLACEHOLDER_TIME' | translate}}"
            autocorrect="off"
diff --git a/guacamole/src/main/webapp/app/form/templates/timeZoneField.html b/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
index ed960af..ecab57d 100644
--- a/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
+++ b/guacamole/src/main/webapp/app/form/templates/timeZoneField.html
@@ -2,13 +2,15 @@
 
     <!-- Available time zone regions -->
     <select class="time-zone-region"
+            ng-disabled="disabled"
+            guac-focus="focused"
             ng-attr-id="{{ fieldId }}"
             ng-model="region"
             ng-options="name for name in regions | orderBy: name"></select>
 
     <!-- Time zones within selected region -->
     <select class="time-zone"
-            ng-disabled="!region"
+            ng-disabled="disabled || !region"
             ng-model="model"
             ng-options="timeZone.value as timeZone.key for timeZone in timeZones[region] | toArray | orderBy: key"></select>
 
diff --git a/guacamole/src/main/webapp/app/groupList/directives/guacGroupListFilter.js b/guacamole/src/main/webapp/app/groupList/directives/guacGroupListFilter.js
index 5a3c6b5..ad1bd43 100644
--- a/guacamole/src/main/webapp/app/groupList/directives/guacGroupListFilter.js
+++ b/guacamole/src/main/webapp/app/groupList/directives/guacGroupListFilter.js
@@ -186,7 +186,7 @@
                 angular.forEach(item.children, function flattenChild(child) {
                     if (child.type === GroupListItem.Type.CONNECTION_GROUP) {
 
-                        var flattenedChild = flattenConnectionGroup(child);
+                        var flattenedChild = flattenGroupListItem(child);
 
                         // Merge all children
                         Array.prototype.push.apply(
diff --git a/guacamole/src/main/webapp/app/groupList/groupListModule.js b/guacamole/src/main/webapp/app/groupList/groupListModule.js
index 14f4706..eb37ac5 100644
--- a/guacamole/src/main/webapp/app/groupList/groupListModule.js
+++ b/guacamole/src/main/webapp/app/groupList/groupListModule.js
@@ -21,4 +21,8 @@
  * Module for displaying the contents of a connection group, allowing the user
  * to select individual connections or groups.
  */
-angular.module('groupList', ['list', 'rest']);
+angular.module('groupList', [
+    'navigation',
+    'list',
+    'rest'
+]);
diff --git a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
index e662972..29bf91b 100644
--- a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
+++ b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js
@@ -20,7 +20,11 @@
 /**
  * Provides the GroupListItem class definition.
  */
-angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', function defineGroupListItem(ConnectionGroup) {
+angular.module('groupList').factory('GroupListItem', ['$injector', function defineGroupListItem($injector) {
+
+    // Required types
+    var ClientIdentifier = $injector.get('ClientIdentifier');
+    var ConnectionGroup  = $injector.get('ConnectionGroup');
 
     /**
      * Creates a new GroupListItem, initializing the properties of that
@@ -109,15 +113,51 @@
 
         /**
          * Returns the number of currently active users for this connection,
-         * connection group, or sharing profile, if known.
+         * connection group, or sharing profile, if known. If unknown, null may
+         * be returned.
          * 
-         * @type Number
+         * @returns {Number}
+         *     The number of currently active users for this connection,
+         *     connection group, or sharing profile.
          */
         this.getActiveConnections = template.getActiveConnections || (function getActiveConnections() {
             return null;
         });
 
         /**
+         * Returns the unique string identifier that must be used when
+         * connecting to a connection or connection group represented by this
+         * GroupListItem.
+         *
+         * @returns {String}
+         *     The client identifier associated with the connection or
+         *     connection group represented by this GroupListItem, or null if
+         *     this GroupListItem cannot have an associated client identifier.
+         */
+        this.getClientIdentifier = template.getClientIdentifier || function getClientIdentifier() {
+
+            // If the item is a connection, generate a connection identifier
+            if (this.type === GroupListItem.Type.CONNECTION)
+                return ClientIdentifier.toString({
+                    dataSource : this.dataSource,
+                    type       : ClientIdentifier.Types.CONNECTION,
+                    id         : this.identifier
+                });
+
+            // If the item is a connection group, generate a connection group identifier
+            if (this.type === GroupListItem.Type.CONNECTION_GROUP)
+                return ClientIdentifier.toString({
+                    dataSource : this.dataSource,
+                    type       : ClientIdentifier.Types.CONNECTION_GROUP,
+                    id         : this.identifier
+                });
+
+            // Otherwise, no such identifier can exist
+            return null;
+
+        };
+
+        /**
          * The connection, connection group, or sharing profile whose data is
          * exposed within this GroupListItem. If the type of this GroupListItem
          * is not one of the types defined by GroupListItem.Type, then this
diff --git a/guacamole/src/main/webapp/app/home/controllers/homeController.js b/guacamole/src/main/webapp/app/home/controllers/homeController.js
index 6c2bd69..a6d359e 100644
--- a/guacamole/src/main/webapp/app/home/controllers/homeController.js
+++ b/guacamole/src/main/webapp/app/home/controllers/homeController.js
@@ -25,7 +25,6 @@
 
     // Get required types
     var ConnectionGroup  = $injector.get('ConnectionGroup');
-    var ClientIdentifier = $injector.get('ClientIdentifier');
     var GroupListItem    = $injector.get('GroupListItem');
             
     // Get required services
@@ -74,51 +73,6 @@
 
     };
 
-    /**
-     * Object passed to the guacGroupList directive, providing context-specific
-     * functions or data.
-     */
-    $scope.context = {
-
-        /**
-         * Returns the unique string identifier which must be used when
-         * connecting to a connection or connection group represented by the
-         * given GroupListItem.
-         *
-         * @param {GroupListItem} item
-         *     The GroupListItem to determine the client identifier of.
-         *
-         * @returns {String}
-         *     The client identifier associated with the connection or
-         *     connection group represented by the given GroupListItem, or null
-         *     if the GroupListItem cannot have an associated client
-         *     identifier.
-         */
-        getClientIdentifier : function getClientIdentifier(item) {
-
-            // If the item is a connection, generate a connection identifier
-            if (item.type === GroupListItem.Type.CONNECTION)
-                return ClientIdentifier.toString({
-                    dataSource : item.dataSource,
-                    type       : ClientIdentifier.Types.CONNECTION,
-                    id         : item.identifier
-                });
-
-            // If the item is a connection group, generate a connection group identifier
-            if (item.type === GroupListItem.Type.CONNECTION_GROUP)
-                return ClientIdentifier.toString({
-                    dataSource : item.dataSource,
-                    type       : ClientIdentifier.Types.CONNECTION_GROUP,
-                    id         : item.identifier
-                });
-
-            // Otherwise, no such identifier can exist
-            return null;
-
-        }
-
-    };
-
     // Retrieve root groups and all descendants
     dataSourceService.apply(
         connectionGroupService.getConnectionGroupTree,
diff --git a/guacamole/src/main/webapp/app/home/templates/connection.html b/guacamole/src/main/webapp/app/home/templates/connection.html
index 3f244fb..b428412 100644
--- a/guacamole/src/main/webapp/app/home/templates/connection.html
+++ b/guacamole/src/main/webapp/app/home/templates/connection.html
@@ -1,5 +1,5 @@
 <a class="home-connection"
-   ng-href="#/client/{{context.getClientIdentifier(item)}}"
+   ng-href="#/client/{{ item.getClientIdentifier() }}"
    ng-class="{active: item.getActiveConnections()}">
 
     <!-- Connection icon -->
diff --git a/guacamole/src/main/webapp/app/home/templates/connectionGroup.html b/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
index f1261dc..909aacf 100644
--- a/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
+++ b/guacamole/src/main/webapp/app/home/templates/connectionGroup.html
@@ -1,4 +1,4 @@
 <span class="home-connection-group name">
-    <a ng-show="item.balancing" ng-href="#/client/{{context.getClientIdentifier(item)}}">{{item.name}}</a>
+    <a ng-show="item.balancing" ng-href="#/client/{{ item.getClientIdentifier() }}">{{item.name}}</a>
     <span ng-show="!item.balancing">{{item.name}}</span>
 </span>
diff --git a/guacamole/src/main/webapp/app/home/templates/home.html b/guacamole/src/main/webapp/app/home/templates/home.html
index f68a0a9..4597dff 100644
--- a/guacamole/src/main/webapp/app/home/templates/home.html
+++ b/guacamole/src/main/webapp/app/home/templates/home.html
@@ -23,7 +23,6 @@
         </div>
         <div class="all-connections">
             <guac-group-list
-                context="context"
                 connection-groups="filteredRootConnectionGroups"
                 templates="{
                     'connection'       : 'app/home/templates/connection.html',
diff --git a/guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js b/guacamole/src/main/webapp/app/index/config/httpDefaults.js
similarity index 66%
copy from guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js
copy to guacamole/src/main/webapp/app/index/config/httpDefaults.js
index 114d598..9de1ce2 100644
--- a/guacamole/src/main/webapp/app/index/config/indexHttpPatchConfig.js
+++ b/guacamole/src/main/webapp/app/index/config/httpDefaults.js
@@ -18,14 +18,19 @@
  */
 
 /**
- * The config block for setting up the HTTP PATCH method.
+ * Defaults for the AngularJS $http service.
  */
-angular.module('index').config(['$httpProvider', 
-        function indexHttpPatchConfig($httpProvider) {
-    
+angular.module('index').config(['$httpProvider', function httpDefaults($httpProvider) {
+
+    // Do not cache the responses of GET requests
+    $httpProvider.defaults.headers.get = {
+        'Cache-Control' : 'no-cache',
+        'Pragma' : 'no-cache'
+    };
+
+    // Use "application/json" content type by default for PATCH requests
     $httpProvider.defaults.headers.patch = {
-        'Content-Type': 'application/json'
-    }
+        'Content-Type' : 'application/json'
+    };
+
 }]);
-
-
diff --git a/guacamole/src/main/webapp/app/login/directives/login.js b/guacamole/src/main/webapp/app/login/directives/login.js
index 562e397..a414548 100644
--- a/guacamole/src/main/webapp/app/login/directives/login.js
+++ b/guacamole/src/main/webapp/app/login/directives/login.js
@@ -66,6 +66,7 @@
         var Field = $injector.get('Field');
 
         // Required services
+        var $rootScope            = $injector.get('$rootScope');
         var $route                = $injector.get('$route');
         var authenticationService = $injector.get('authenticationService');
         var requestService        = $injector.get('requestService');
@@ -92,6 +93,23 @@
         $scope.remainingFields = [];
 
         /**
+         * Whether an authentication attempt has been submitted. This will be
+         * set to true once credentials have been submitted and will only be
+         * reset to false once the attempt has been fully processed, including
+         * rerouting the user to the requested page if the attempt succeeded.
+         *
+         * @type Boolean
+         */
+        $scope.submitted = false;
+
+        /**
+         * The field that is most relevant to the user.
+         *
+         * @type Field
+         */
+        $scope.relevantField = null;
+
+        /**
          * Returns whether a previous login attempt is continuing.
          *
          * @return {Boolean}
@@ -133,6 +151,8 @@
                     $scope.enteredValues[field.name] = '';
             });
 
+            $scope.relevantField = getRelevantField();
+
         });
 
         /**
@@ -141,21 +161,27 @@
          */
         $scope.login = function login() {
 
+            // Authentication is now in progress
+            $scope.submitted = true;
+
             // Start with cleared status
-            $scope.loginError  = null;
+            $scope.loginError = null;
 
             // Attempt login once existing session is destroyed
             authenticationService.authenticate($scope.enteredValues)
 
-            // Clear and reload upon success
+            // Retry route upon success (entered values will be cleared only
+            // after route change has succeeded as this can take time)
             .then(function loginSuccessful() {
-                $scope.enteredValues = {};
                 $route.reload();
             })
 
             // Reset upon failure
             ['catch'](requestService.createErrorCallback(function loginFailed(error) {
 
+                // Initial submission is complete and has failed
+                $scope.submitted = false;
+
                 // Clear out passwords if the credentials were rejected for any reason
                 if (error.type !== Error.Type.INSUFFICIENT_CREDENTIALS) {
 
@@ -183,6 +209,32 @@
 
         };
 
+        /**
+         * Returns the field most relevant to the user given the current state
+         * of the login process. This will normally be the first empty field.
+         *
+         * @return {Field}
+         *     The field most relevant, null if there is no single most relevant
+         *     field.
+         */
+        var getRelevantField = function getRelevantField() {
+
+            for (var i = 0; i < $scope.remainingFields.length; i++) {
+                var field = $scope.remainingFields[i];
+                if (!$scope.enteredValues[field.name])
+                    return field;
+            }
+
+            return null;
+
+        };
+
+        // Reset state after authentication and routing have succeeded
+        $rootScope.$on('$routeChangeSuccess', function routeChanged() {
+            $scope.enteredValues = {};
+            $scope.submitted = false;
+        });
+
     }];
 
     return directive;
diff --git a/guacamole/src/main/webapp/app/login/styles/dialog.css b/guacamole/src/main/webapp/app/login/styles/dialog.css
index c9c6a4a..e833533 100644
--- a/guacamole/src/main/webapp/app/login/styles/dialog.css
+++ b/guacamole/src/main/webapp/app/login/styles/dialog.css
@@ -17,27 +17,20 @@
  * under the License.
  */
 
-.login-ui.error .login-dialog {
-    animation-name: shake-head;
-    animation-duration: 0.25s;
-    animation-timing-function: linear;
-    -webkit-animation-name: shake-head;
-    -webkit-animation-duration: 0.25s;
-    -webkit-animation-timing-function: linear;
+.login-ui {
+    animation: fadein 0.125s linear;
+    -moz-animation: fadein 0.125s linear;
+    -webkit-animation: fadein 0.125s linear;
 }
 
-.login-ui div.login-dialog-middle {
+.login-ui .login-dialog-middle {
     width: 100%;
     display: table-cell;
     vertical-align: middle;
     text-align: center;
 }
 
-.login-ui div.login-dialog {
-
-    animation: fadein 0.125s linear;
-    -moz-animation: fadein 0.125s linear;
-    -webkit-animation: fadein 0.125s linear;
+.login-ui .login-dialog {
 
     width: 100%;
     max-width: 3in;
@@ -115,7 +108,7 @@
     background-image: url("images/guac-tricolor.png");
 }
 
-.login-ui.continuation div.login-dialog {
+.login-ui.continuation .login-dialog {
     border-right: none;
     border-left: none;
     box-shadow: none;
@@ -126,3 +119,12 @@
 .login-ui.continuation .login-dialog .version {
     display: none;
 }
+
+.login-ui.error .login-dialog {
+    animation-name: shake-head;
+    animation-duration: 0.25s;
+    animation-timing-function: linear;
+    -webkit-animation-name: shake-head;
+    -webkit-animation-duration: 0.25s;
+    -webkit-animation-timing-function: linear;
+}
diff --git a/guacamole/src/main/webapp/app/login/templates/login.html b/guacamole/src/main/webapp/app/login/templates/login.html
index 26a3f18..04111ed 100644
--- a/guacamole/src/main/webapp/app/login/templates/login.html
+++ b/guacamole/src/main/webapp/app/login/templates/login.html
@@ -23,13 +23,25 @@
 
                 <!-- Login fields -->
                 <div class="login-fields">
-                    <guac-form namespace="'LOGIN'" content="remainingFields" model="enteredValues"></guac-form>
+                    <guac-form
+                        namespace="'LOGIN'"
+                        content="remainingFields"
+                        model="enteredValues"
+                        focused="relevantField.name"
+                        data-disabled="submitted"></guac-form>
                 </div>
 
-                <!-- Submit button -->
+                <!-- Login/continue button -->
                 <div class="buttons">
-                    <input type="submit" name="login" class="login" value="{{'LOGIN.ACTION_LOGIN' | translate}}"/>
-                    <input type="submit" name="login" class="continue-login" value="{{'LOGIN.ACTION_CONTINUE' | translate}}"/>
+
+                    <input type="submit" name="login" class="login"
+                           ng-disabled="submitted"
+                           value="{{'LOGIN.ACTION_LOGIN' | translate}}"/>
+
+                    <input type="submit" name="login" class="continue-login"
+                           ng-disabled="submitted"
+                           value="{{'LOGIN.ACTION_CONTINUE' | translate}}"/>
+
                 </div>
 
             </form>
diff --git a/guacamole/src/main/webapp/app/navigation/directives/guacMenu.js b/guacamole/src/main/webapp/app/navigation/directives/guacMenu.js
index e00cc1c..230e902 100644
--- a/guacamole/src/main/webapp/app/navigation/directives/guacMenu.js
+++ b/guacamole/src/main/webapp/app/navigation/directives/guacMenu.js
@@ -34,7 +34,16 @@
              *
              * @type String
              */
-            menuTitle : '='
+            menuTitle : '=',
+
+            /**
+             * Whether the menu should remain open while the user interacts
+             * with the contents of the menu. By default, the menu will close
+             * if the user clicks within the menu contents.
+             *
+             * @type Boolean
+             */
+            interactive : '='
 
         },
 
@@ -53,6 +62,14 @@
             var element = $element[0];
 
             /**
+             * The element containing the menu contents that display when the
+             * menu is open.
+             *
+             * @type Element
+             */
+            var contents = $element.find('.menu-contents')[0];
+
+            /**
              * The main document object.
              *
              * @type Document
@@ -73,16 +90,19 @@
                 $scope.menuShown = !$scope.menuShown;
             };
 
-            // Close menu when use clicks anywhere else
-            document.body.addEventListener('click', function clickOutsideMenu() {
+            // Close menu when user clicks anywhere outside this specific menu
+            document.body.addEventListener('click', function clickOutsideMenu(e) {
                 $scope.$apply(function closeMenu() {
-                    $scope.menuShown = false;
+                    if (e.target !== element && !element.contains(e.target))
+                        $scope.menuShown = false;
                 });
             }, false);
 
-            // Prevent click within menu from triggering the outside-menu handler
-            element.addEventListener('click', function clickInsideMenu(e) {
-                e.stopPropagation();
+            // Prevent clicks within menu contents from toggling menu visibility
+            // if the menu contents are intended to be interactive
+            contents.addEventListener('click', function clickInsideMenuContents(e) {
+                if ($scope.interactive)
+                    e.stopPropagation();
             }, false);
 
         }] // end controller
diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
index 2af426e..f91303a 100644
--- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js
+++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
@@ -67,7 +67,6 @@
      */
     var generateHomePage = function generateHomePage(rootGroups, permissions) {
 
-        var homePage = null;
         var settingsPages = generateSettingsPages(permissions);
 
         // If user has access to settings pages, return home page and skip
@@ -79,67 +78,87 @@
         if (settingsPages.length > 2)
             return SYSTEM_HOME_PAGE;
 
+        // If exactly one connection or balancing group is available, use
+        // that as the home page
+        var clientPages = service.getClientPages(rootGroups);
+        return (clientPages.length === 1) ? clientPages[0] : SYSTEM_HOME_PAGE;
+
+    };
+
+    /**
+     * Adds to the given array all pages that the current user may use to
+     * access connections or balancing groups that are descendants of the given
+     * connection group.
+     *
+     * @param {PageDefinition[]} clientPages
+     *     The array that pages should be added to.
+     *
+     * @param {String} dataSource
+     *     The data source containing the given connection group.
+     *
+     * @param {ConnectionGroup} connectionGroup
+     *     The connection group ancestor of the connection or balancing group
+     *     descendants whose pages should be added to the given array.
+     */
+    var addClientPages = function addClientPages(clientPages, dataSource, connectionGroup) {
+
+        // Add pages for all child connections
+        angular.forEach(connectionGroup.childConnections, function addConnectionPage(connection) {
+            clientPages.push(new PageDefinition({
+                name : connection.name,
+                url  : '/client/' + ClientIdentifier.toString({
+                    dataSource : dataSource,
+                    type       : ClientIdentifier.Types.CONNECTION,
+                    id         : connection.identifier
+                })
+            }));
+        });
+
+        // Add pages for all child balancing groups, as well as the connectable
+        // descendants of all balancing groups of any type
+        angular.forEach(connectionGroup.childConnectionGroups, function addConnectionGroupPage(connectionGroup) {
+
+            if (connectionGroup.type === ConnectionGroup.Type.BALANCING) {
+                clientPages.push(new PageDefinition({
+                    name : connectionGroup.name,
+                    url  : '/client/' + ClientIdentifier.toString({
+                        dataSource : dataSource,
+                        type       : ClientIdentifier.Types.CONNECTION_GROUP,
+                        id         : connectionGroup.identifier
+                    })
+                }));
+            }
+
+            addClientPages(clientPages, dataSource, connectionGroup);
+
+        });
+
+    };
+
+    /**
+     * Returns a full list of all pages that the current user may use to access
+     * a connection or balancing group, regardless of the depth of those
+     * connections/groups within the connection hierarchy.
+     *
+     * @param {Object.<String, ConnectionGroup>} rootGroups
+     *     A map of all root connection groups visible to the current user,
+     *     where each key is the identifier of the corresponding data source.
+     *
+     * @returns {PageDefinition[]}
+     *     A list of all pages that the current user may use to access a
+     *     connection or balancing group.
+     */
+    service.getClientPages = function getClientPages(rootGroups) {
+
+        var clientPages = [];
+
         // Determine whether a connection or balancing group should serve as
         // the home page
         for (var dataSource in rootGroups) {
+            addClientPages(clientPages, dataSource, rootGroups[dataSource]);
+        }
 
-            // Get corresponding root group
-            var rootGroup = rootGroups[dataSource];
-
-            // Get children
-            var connections      = rootGroup.childConnections      || [];
-            var connectionGroups = rootGroup.childConnectionGroups || [];
-
-            // Calculate total number of root-level objects
-            var totalRootObjects = connections.length + connectionGroups.length;
-
-            // If exactly one connection or balancing group is available, use
-            // that as the home page
-            if (homePage === null && totalRootObjects === 1) {
-
-                var connection      = connections[0];
-                var connectionGroup = connectionGroups[0];
-
-                // Only one connection present, use as home page
-                if (connection) {
-                    homePage = new PageDefinition({
-                        name : connection.name,
-                        url  : '/client/' + ClientIdentifier.toString({
-                            dataSource : dataSource,
-                            type       : ClientIdentifier.Types.CONNECTION,
-                            id         : connection.identifier
-                        })
-                    });
-                }
-
-                // Only one balancing group present, use as home page
-                if (connectionGroup
-                        && connectionGroup.type === ConnectionGroup.Type.BALANCING
-                        && _.isEmpty(connectionGroup.childConnections)
-                        && _.isEmpty(connectionGroup.childConnectionGroups)) {
-                    homePage = new PageDefinition({
-                        name : connectionGroup.name,
-                        url  : '/client/' + ClientIdentifier.toString({
-                            dataSource : dataSource,
-                            type       : ClientIdentifier.Types.CONNECTION_GROUP,
-                            id         : connectionGroup.identifier
-                        })
-                    });
-                }
-
-            }
-
-            // Otherwise, a connection or balancing group cannot serve as the
-            // home page
-            else if (totalRootObjects >= 1) {
-                homePage = null;
-                break;
-            }
-
-        } // end for each data source
-
-        // Use default home page if no other is available
-        return homePage || SYSTEM_HOME_PAGE;
+        return clientPages;
 
     };
 
diff --git a/guacamole/src/main/webapp/app/navigation/styles/menu.css b/guacamole/src/main/webapp/app/navigation/styles/menu.css
index 1e4e75e..65e010b 100644
--- a/guacamole/src/main/webapp/app/navigation/styles/menu.css
+++ b/guacamole/src/main/webapp/app/navigation/styles/menu.css
@@ -68,6 +68,11 @@
     padding: 0.5em;
     padding-right: 2em;
 
+    white-space: nowrap;
+    overflow: hidden;
+    width: 100%;
+    text-overflow: ellipsis;
+
     -ms-flex: 0 0 auto;
     -moz-box-flex: 0;
     -webkit-box-flex: 0;
diff --git a/guacamole/src/main/webapp/images/arrows/left.png b/guacamole/src/main/webapp/images/arrows/left.png
new file mode 100644
index 0000000..920a4fd
--- /dev/null
+++ b/guacamole/src/main/webapp/images/arrows/left.png
Binary files differ
diff --git a/guacamole/src/main/webapp/images/warning-white.png b/guacamole/src/main/webapp/images/warning-white.png
new file mode 100644
index 0000000..7254ea7
--- /dev/null
+++ b/guacamole/src/main/webapp/images/warning-white.png
Binary files differ
diff --git a/guacamole/src/main/webapp/translations/cz.json b/guacamole/src/main/webapp/translations/cz.json
new file mode 100644
index 0000000..6f406d0
--- /dev/null
+++ b/guacamole/src/main/webapp/translations/cz.json
@@ -0,0 +1,883 @@
+{
+
+    "NAME" : "Čeština",
+
+    "APP" : {
+
+        "ACTION_ACKNOWLEDGE"        : "OK",
+        "ACTION_CANCEL"             : "Zrušit",
+        "ACTION_CLONE"              : "Klonovat",
+        "ACTION_CONTINUE"           : "Pokračovat",
+        "ACTION_DELETE"             : "Smazat",
+        "ACTION_DELETE_SESSIONS"    : "Ukončit sezení",
+        "ACTION_DOWNLOAD"           : "Stáhnout",
+        "ACTION_LOGIN"              : "Přihlásit",
+        "ACTION_LOGOUT"             : "Odhlásit",
+        "ACTION_MANAGE_CONNECTIONS" : "Připojení",
+        "ACTION_MANAGE_PREFERENCES" : "Vlastnosti",
+        "ACTION_MANAGE_SETTINGS"    : "Nastavení",
+        "ACTION_MANAGE_SESSIONS"    : "Aktivní sezení",
+        "ACTION_MANAGE_USERS"       : "Uživatelé",
+        "ACTION_MANAGE_USER_GROUPS" : "Skupiny",
+        "ACTION_NAVIGATE_BACK"      : "Zpět",
+        "ACTION_NAVIGATE_HOME"      : "Domů",
+        "ACTION_SAVE"               : "Uložit",
+        "ACTION_SEARCH"             : "Hledat",
+        "ACTION_SHARE"              : "Sdílet",
+        "ACTION_UPDATE_PASSWORD"    : "Změnit heslo",
+        "ACTION_VIEW_HISTORY"       : "Historie",
+
+        "DIALOG_HEADER_ERROR" : "Chyba",
+
+        "ERROR_PAGE_UNAVAILABLE"  : "Došlo k chybě a tuto akci nelze dokončit. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+        "ERROR_PASSWORD_BLANK"    : "Heslo nesmí být prázdné.",
+        "ERROR_PASSWORD_MISMATCH" : "Hesla nesouhlasí.",
+
+        "FIELD_HEADER_PASSWORD"       : "Heslo:",
+        "FIELD_HEADER_PASSWORD_AGAIN" : "Heslo znovu:",
+
+        "FIELD_PLACEHOLDER_FILTER"    : "Filtr",
+
+        "FORMAT_DATE_TIME_PRECISE" : "yyyy-MM-dd HH:mm:ss",
+
+        "INFO_ACTIVE_USER_COUNT" : "V současné době používá {USERS} {USERS, plural, one{user} other{users}}.",
+
+        "TEXT_ANONYMOUS_USER"   : "Anonym",
+        "TEXT_HISTORY_DURATION" : "{VALUE} {UNIT, select, second{{VALUE, plural, one{second} other{seconds}}} minute{{VALUE, plural, one{minute} other{minutes}}} hour{{VALUE, plural, one{hour} other{hours}}} day{{VALUE, plural, one{day} other{days}}} other{}}"
+
+    },
+
+    "CLIENT" : {
+
+        "ACTION_ACKNOWLEDGE"               : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CLEAR_COMPLETED_TRANSFERS" : "Vyčistit",
+        "ACTION_DISCONNECT"                : "Odpojit",
+        "ACTION_LOGOUT"                    : "@:APP.ACTION_LOGOUT",
+        "ACTION_NAVIGATE_BACK"             : "@:APP.ACTION_NAVIGATE_BACK",
+        "ACTION_NAVIGATE_HOME"             : "@:APP.ACTION_NAVIGATE_HOME",
+        "ACTION_RECONNECT"                 : "Znovu připojit",
+        "ACTION_SAVE_FILE"                 : "@:APP.ACTION_SAVE",
+        "ACTION_SHARE"                     : "@:APP.ACTION_SHARE",
+        "ACTION_UPLOAD_FILES"              : "Nahrát soubory",
+
+        "DIALOG_HEADER_CONNECTING"       : "Připojování",
+        "DIALOG_HEADER_CONNECTION_ERROR" : "Chyba připojení",
+        "DIALOG_HEADER_DISCONNECTED"     : "Odpojeno",
+
+        "ERROR_CLIENT_201"     : "Synchronizační server je zaneprázdněn, zkuste to prosím znovu později.",
+        "ERROR_CLIENT_202"     : "Guacamole server zavřel spojení protože vzdálený počítač příliš dlouho neodpovídal. Zkuste to prosím později, nebo kontaktujte správce.",
+        "ERROR_CLIENT_203"     : "Chyba vzdáleného serveru, spojení bylo uzavřeno. Zkuste to prosím později, nebo kontaktujte správce.",
+        "ERROR_CLIENT_207"     : "Server vzdálené plochy je aktuálně nedostupný. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+        "ERROR_CLIENT_208"     : "Server vzdálené plochy není aktuálně k dispozici. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+        "ERROR_CLIENT_209"     : "Server vzdálené plochy ukončil připojení, protože je v konfliktu s jiným připojením. Prosím zkuste to znovu později.",
+        "ERROR_CLIENT_20A"     : "Server vzdálené plochy ukončil připojení, protože se zdálo být neaktivní. Pokud je to nežádoucí nebo neočekávané, informujte prosím správce systému nebo zkontrolujte nastavení systému.",
+        "ERROR_CLIENT_20B"     : "Server vzdálené plochy násilně uzavřel připojení. Pokud je to nežádoucí nebo neočekávané, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+        "ERROR_CLIENT_301"     : "Přihlášení selhalo. Připojte se a zkuste to znovu.",
+        "ERROR_CLIENT_303"     : "Server vzdálené plochy odepřel přístup k tomuto připojení. Pokud potřebujete přístup, požádejte správce systému, aby vám umožnil přístup, nebo zkontrolujte nastavení systému.",
+        "ERROR_CLIENT_308"     : "Server Guacamole ukončil spojení, protože z vašeho prohlížeče nebyla dostatečně dlouhá odezva, aby se zdálo, že je stále připojen. To je obvykle způsobeno problémy se sítí, jako je například nekvalitní bezdrátový signál, nebo jednoduše velmi pomalá síťová rychlost. Zkontrolujte síť a zkuste to znovu.",
+        "ERROR_CLIENT_31D"     : "Server Guacamole odepřel přístup k tomuto připojení, protože jste vyčerpali limit pro vícenásobné připojení tímto uživatelem. Zavřete jedno nebo více připojení a zkuste to znovu.",
+        "ERROR_CLIENT_DEFAULT" : "V rámci serveru Guacamole došlo k interní chybě a připojení bylo ukončeno. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+
+        "ERROR_TUNNEL_201"     : "Server Guacamole odmítl tento pokus o připojení, protože existuje příliš mnoho aktivních připojení. Počkejte prosím několik minut a zkuste to znovu.",
+        "ERROR_TUNNEL_202"     : "Připojení bylo uzavřeno, protože serveru trvalo příliš dlouho, než odpověděl. To je obvykle způsobeno problémy se sítí, jako je například nekvalitní bezdrátový signál nebo pomalé připojení k síti. Zkontrolujte síťové připojení a zkuste to znovu nebo se obraťte na správce systému.",
+        "ERROR_TUNNEL_203"     : "Server zjistil chybu a připojení ukončil. Zkuste to prosím znovu nebo se obraťte na správce systému.",
+        "ERROR_TUNNEL_204"     : "Požadované připojení neexistuje. Zkontrolujte název připojení a zkuste to znovu.",
+        "ERROR_TUNNEL_205"     : "Toto připojení je právě používáno a vícenásobný přístup k tomuto připojení není povolen. Prosím zkuste to znovu později.",
+        "ERROR_TUNNEL_207"     : "Server Guacamole není v současné době dostupný. Zkontrolujte síť a zkuste to znovu",
+        "ERROR_TUNNEL_208"     : "Server Guacamole nepřijímá připojení. Zkontrolujte síť a zkuste to znovu.",
+        "ERROR_TUNNEL_301"     : "Nemáte oprávnění k přístupu k tomuto připojení, protože nejste přihlášeni. Přihlaste se a zkuste to znovu.",
+        "ERROR_TUNNEL_303"     : "Nemáte oprávnění k přístupu k tomuto připojení. Pokud potřebujete přístup, požádejte správce systému, aby vás přidal do seznamu povolených uživatelů nebo zkontrolujte nastavení systému.",
+        "ERROR_TUNNEL_308"     : "Server Guacamole ukončil spojení, protože z vašeho prohlížeče nebyla dostatečně dlouhá odezva, aby se zdálo, že je stále připojen. To je obvykle způsobeno problémy se sítí, jako je například nekvalitní bezdrátový signál, nebo jednoduše velmi pomalá síťová rychlost. Zkontrolujte síť a zkuste to znovu.",
+        "ERROR_TUNNEL_31D"     : "Server Guacamole odepřel přístup k tomuto připojení, protože jste vyčerpali limit pro vícenásobné připojení tímto uživatelem. Zavřete jedno nebo více připojení a zkuste to znovu.",
+        "ERROR_TUNNEL_DEFAULT" : "V rámci serveru Guacamole došlo k interní chybě a připojení bylo ukončeno. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+
+        "ERROR_UPLOAD_100"     : "Přenos souborů není podporován nebo není povolen. Obraťte se na správce systému nebo zkontrolujte systémové protokoly.",
+        "ERROR_UPLOAD_201"     : "V současné době se přenáší příliš mnoho souborů. Počkejte prosím na dokončení probíhajících přenosů a akci opakujte.",
+        "ERROR_UPLOAD_202"     : "Soubor nelze přenést, protože serveru vzdálené plochy trvá příliš dlouho, než odpoví. Zkuste to prosím znovu nebo se obraťte na správce systému.",
+        "ERROR_UPLOAD_203"     : "Server vzdálené plochy zaznamenal chybu během přenosu. Zkuste to prosím znovu nebo se obraťte na správce systému.",
+        "ERROR_UPLOAD_204"     : "Cíl pro přenos souborů neexistuje. Zkontrolujte, zda cíl existuje a zkuste to znovu.",
+        "ERROR_UPLOAD_205"     : "Cíl přenosu souborů je aktuálně uzamčen. Počkejte prosím na dokončení probíhajících úkolů a zkuste to znovu.",
+        "ERROR_UPLOAD_301"     : "Nemáte oprávnění nahrát tento soubor, protože nejste přihlášeni. Přihlaste se a zkuste to znovu.",
+        "ERROR_UPLOAD_303"     : "Nemáte oprávnění k nahrání tohoto souboru. Pokud potřebujete přístup, zkontrolujte nastavení systému nebo se obraťte na správce systému.",
+        "ERROR_UPLOAD_308"     : "Přenos souboru se zastavil. To je obvykle způsobeno problémy se sítí, jako je například nekvalitní bezdrátový signál, nebo jednoduše velmi pomalé připojení k síťi. Zkontrolujte síť a zkuste to znovu.",
+        "ERROR_UPLOAD_31D"     : "V současné době se přenáší příliš mnoho souborů. Počkejte prosím na dokončení probíhajících přenosů a akci opakujte.",
+        "ERROR_UPLOAD_DEFAULT" : "V rámci serveru Guacamole došlo k interní chybě a připojení bylo ukončeno. Pokud problém přetrvává, informujte prosím správce systému nebo zkontrolujte systémové protokoly.",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_CLIPBOARD"           : "Zde se zobrazí text zkopírovaný / oříznutý v Guacamole. Změny níže uvedeného textu ovlivní vzdálenou schránku.",
+        "HELP_INPUT_METHOD_NONE"   : "Není použita žádná metoda vstupu. Vstup z klávesnice je přijímán z připojené fyzické klávesnice.",
+        "HELP_INPUT_METHOD_OSK"    : "Zobrazte a přijměte vstup z vestavěné klávesnice Guacamole na obrazovce. Klávesnice na obrazovce umožňuje zadávat kombinace kláves, které jinak mohou být nemožné (například Ctrl-Alt-Del).",
+        "HELP_INPUT_METHOD_TEXT"   : "Povolit psaní textu a emulovat události klávesnice na základě zadaného textu. To je nezbytné pro zařízení, jako jsou mobilní telefony, které nemají fyzickou klávesnici.",
+        "HELP_MOUSE_MODE"          : "Určuje, jak se bude vzdálená myš chovat s ohledem na dotyky.",
+        "HELP_MOUSE_MODE_ABSOLUTE" : "Tap to click. Kliknutí nastane v místě dotyku.",
+        "HELP_MOUSE_MODE_RELATIVE" : "Přetažením myši posuňte ukazatel myši a klepněte na tlačítko. Kliknutí nastane v místě ukazatele.",
+        "HELP_SHARE_LINK"          : "Aktuální připojení je sdíleno a může k němu přistupovat kdokoli s následujícím {LINKS, plural, one{link} other{links}}:",
+
+        "INFO_CONNECTION_SHARED" : "Toto připojení je nyní sdíleno.",
+        "INFO_NO_FILE_TRANSFERS" : "Žádné přenosy souborů.",
+
+        "NAME_INPUT_METHOD_NONE"   : "Žádné",
+        "NAME_INPUT_METHOD_OSK"    : "Na obrazovce, klávesnice",
+        "NAME_INPUT_METHOD_TEXT"   : "Textový vstup",
+        "NAME_KEY_CTRL"            : "Ctrl",
+        "NAME_KEY_ALT"             : "Alt",
+        "NAME_KEY_ESC"             : "Esc",
+        "NAME_KEY_TAB"             : "Tab",
+        "NAME_MOUSE_MODE_ABSOLUTE" : "Dotyková obrazovka",
+        "NAME_MOUSE_MODE_RELATIVE" : "Touchpad",
+
+        "SECTION_HEADER_CLIPBOARD"      : "Schránka",
+        "SECTION_HEADER_DEVICES"        : "Zařízení",
+        "SECTION_HEADER_DISPLAY"        : "Zobrazení",
+        "SECTION_HEADER_FILE_TRANSFERS" : "Přenos souborů",
+        "SECTION_HEADER_INPUT_METHOD"   : "Metoda vstupu",
+        "SECTION_HEADER_MOUSE_MODE"     : "Mód emulace myši",
+
+        "TEXT_ZOOM_AUTO_FIT"              : "Automaticky přizpůsobit prohlížeč oknu",
+        "TEXT_CLIENT_STATUS_IDLE"         : "Nečinný",
+        "TEXT_CLIENT_STATUS_CONNECTING"   : "Připojuji ke Guacamole...",
+        "TEXT_CLIENT_STATUS_DISCONNECTED" : "Byl jste odpojen.",
+        "TEXT_CLIENT_STATUS_UNSTABLE"     : "Síťové spojení ke  Guacamole serveru se zdá nestabilní.",
+        "TEXT_CLIENT_STATUS_WAITING"      : "Připojen ke Guacamole. Čekání na odpověď...",
+        "TEXT_RECONNECT_COUNTDOWN"        : "Znovu připojuji  {REMAINING} {REMAINING, plural, one{second} other{seconds}}...",
+        "TEXT_FILE_TRANSFER_PROGRESS"     : "{PROGRESS} {UNIT, select, b{B} kb{KB} mb{MB} gb{GB} other{}}",
+
+        "URL_OSK_LAYOUT" : "layouts/en-us-qwerty.json"
+
+    },
+
+    "DATA_SOURCE_DEFAULT" : {
+        "NAME" : "Výchozí (XML)"
+    },
+
+    "FORM" : {
+
+        "FIELD_PLACEHOLDER_DATE" : "YYYY-MM-dd",
+        "FIELD_PLACEHOLDER_TIME" : "HH:MM:SS",
+
+        "HELP_SHOW_PASSWORD" : "Klikněte pro zobrazní hesla",
+        "HELP_HIDE_PASSWORD" : "Klikněte pro skrytí hesla"
+
+    },
+
+    "HOME" : {
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
+
+        "INFO_NO_RECENT_CONNECTIONS" : "Žádná nedávná spojení.",
+
+        "PASSWORD_CHANGED" : "Heslo bylo změněno.",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"    : "Všechna spojení",
+        "SECTION_HEADER_RECENT_CONNECTIONS" : "Nedávná spojení"
+
+    },
+
+    "LIST" : {
+
+        "TEXT_ANONYMOUS_USER" : "Anonymní"
+
+    },
+
+    "LOGIN" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CONTINUE"    : "@:APP.ACTION_CONTINUE",
+        "ACTION_LOGIN"       : "@:APP.ACTION_LOGIN",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_INVALID_LOGIN" : "Neplatné přihlašovací jméno",
+
+        "FIELD_HEADER_USERNAME" : "Uživatelské jméno",
+        "FIELD_HEADER_PASSWORD" : "Heslo"
+
+    },
+
+    "MANAGE_CONNECTION" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Smazat spojení",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_LOCATION" : "Oblast:",
+        "FIELD_HEADER_NAME"     : "Jméno:",
+        "FIELD_HEADER_PROTOCOL" : "Protokol:",
+
+        "FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "INFO_CONNECTION_DURATION_UNKNOWN" : "--",
+        "INFO_CONNECTION_ACTIVE_NOW"       : "Aktivní nyní",
+        "INFO_CONNECTION_NOT_USED"         : "Toto spojení ještě nebylo použito.",
+
+        "SECTION_HEADER_EDIT_CONNECTION" : "Upravit spojení",
+        "SECTION_HEADER_HISTORY"         : "Historie využítí",
+        "SECTION_HEADER_PARAMETERS"      : "Parametry",
+
+        "TABLE_HEADER_HISTORY_USERNAME"   : "Uživatelské jméno",
+        "TABLE_HEADER_HISTORY_START"      : "Čas začátku",
+        "TABLE_HEADER_HISTORY_DURATION"   : "Doba",
+        "TABLE_HEADER_HISTORY_REMOTEHOST" : "Vzdálený host",
+
+        "TEXT_CONFIRM_DELETE"   : "Spojení nemůže být obnoveno poté, co je smazáno. Opravdu chcete smazat toto spojení?",
+        "TEXT_HISTORY_DURATION" : "@:APP.TEXT_HISTORY_DURATION"
+
+    },
+
+    "MANAGE_CONNECTION_GROUP" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Smazat skupinu spojení",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_LOCATION" : "Lokace:",
+        "FIELD_HEADER_NAME"     : "Jméno:",
+        "FIELD_HEADER_TYPE"     : "Typ:",
+
+        "NAME_TYPE_BALANCING"      : "Vyvažování:",
+        "NAME_TYPE_ORGANIZATIONAL" : "Organizace:",
+
+        "SECTION_HEADER_EDIT_CONNECTION_GROUP" : "Upravit skupinu spojení",
+
+        "TEXT_CONFIRM_DELETE" : "Skupiny spojení nelze obnovit po jejich odstranění. Opravdu chcete odstranit tuto skupinu připojení?"
+
+    },
+
+    "MANAGE_SHARING_PROFILE" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Odstranit profil sdílení",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_NAME"               : "Jméno:",
+        "FIELD_HEADER_PRIMARY_CONNECTION" : "Primární spojení:",
+
+        "SECTION_HEADER_EDIT_SHARING_PROFILE" : "Upravit sdílený profil",
+        "SECTION_HEADER_PARAMETERS"           : "Parametry",
+
+        "TEXT_CONFIRM_DELETE" : "Po smazání nelze obnovit profily sdílení. Opravdu chcete smazat tento profil sdílení?"
+
+    },
+
+    "MANAGE_USER" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Smazat uživatele",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
+
+        "FIELD_HEADER_ADMINISTER_SYSTEM"            : "Spravovat systém:",
+        "FIELD_HEADER_CHANGE_OWN_PASSWORD"          : "Změnit vlastní heslo:",
+        "FIELD_HEADER_CREATE_NEW_USERS"             : "Vytvořit nové uživatele:",
+        "FIELD_HEADER_CREATE_NEW_USER_GROUPS"       : "Vytvořit novou uživatelskou skupinu:",
+        "FIELD_HEADER_CREATE_NEW_CONNECTIONS"       : "Vytvořit nové spojení:",
+        "FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS" : "Vytvořit nové skupiny připojení:",
+        "FIELD_HEADER_CREATE_NEW_SHARING_PROFILES"  : "Vytvořit nový sdílený profil:",
+        "FIELD_HEADER_PASSWORD"                     : "@:APP.FIELD_HEADER_PASSWORD",
+        "FIELD_HEADER_PASSWORD_AGAIN"               : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
+        "FIELD_HEADER_USERNAME"                     : "Uživatelské jméno:",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_NO_USER_GROUPS" : "Tento uživatel momentálně nepatří do žádné skupiny. Rozbalte tuto sekci, abyste mohli přidát skupiny.",
+
+        "INFO_READ_ONLY"                : "Omlouváme se, ale tento uživatelský účet nelze upravovat.",
+        "INFO_NO_USER_GROUPS_AVAILABLE" : "Nejsou k dispozici žádné skupiny.",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"     : "Všechna připojení",
+        "SECTION_HEADER_CONNECTIONS"         : "Připojení",
+        "SECTION_HEADER_CURRENT_CONNECTIONS" : "Aktuální připojení",
+        "SECTION_HEADER_EDIT_USER"           : "Upravit uživatele",
+        "SECTION_HEADER_PERMISSIONS"         : "Oprávnění",
+        "SECTION_HEADER_USER_GROUPS"         : "Skupiny",
+
+        "TEXT_CONFIRM_DELETE" : "Po odstranění nelze uživatele obnovit. Opravdu chcete tohoto uživatele smazat?"
+
+    },
+
+    "MANAGE_USER_GROUP" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Smazat skupinu",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_ADMINISTER_SYSTEM"            : "@:MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
+        "FIELD_HEADER_CHANGE_OWN_PASSWORD"          : "@:MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD",
+        "FIELD_HEADER_CREATE_NEW_USERS"             : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
+        "FIELD_HEADER_CREATE_NEW_USER_GROUPS"       : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USER_GROUPS",
+        "FIELD_HEADER_CREATE_NEW_CONNECTIONS"       : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
+        "FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
+        "FIELD_HEADER_CREATE_NEW_SHARING_PROFILES"  : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES",
+        "FIELD_HEADER_USER_GROUP_NAME"              : "Jméno skupiny:",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_NO_USER_GROUPS"        : "Tato skupina momentálně nepatří do žádné skupiny. Rozbalením této sekce ji přidáte do skupiny.",
+        "HELP_NO_MEMBER_USER_GROUPS" : "Tato skupina v současné době neobsahuje žádné skupiny. Rozbalením této sekce ji přidáte do skupiny.",
+        "HELP_NO_MEMBER_USERS"       : "Tato skupina v současné době neobsahuje žádné uživatele. Rozbalením této sekce přidáte uživatele.",
+
+        "INFO_READ_ONLY"                : "Je nám líto, ale tuto skupinu nelze upravovat.",
+        "INFO_NO_USER_GROUPS_AVAILABLE" : "@:MANAGE_USER.INFO_NO_USER_GROUPS_AVAILABLE",
+        "INFO_NO_USERS_AVAILABLE"       : "Žádní uživatelé nejsou k dispozici.",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"     : "@:MANAGE_USER.SECTION_HEADER_ALL_CONNECTIONS",
+        "SECTION_HEADER_CONNECTIONS"         : "@:MANAGE_USER.SECTION_HEADER_CONNECTIONS",
+        "SECTION_HEADER_CURRENT_CONNECTIONS" : "@:MANAGE_USER.SECTION_HEADER_CURRENT_CONNECTIONS",
+        "SECTION_HEADER_EDIT_USER_GROUP"     : "Upravit skupinu",
+        "SECTION_HEADER_MEMBER_USERS"        : "Členský uživatel",
+        "SECTION_HEADER_MEMBER_USER_GROUPS"  : "Členské skupiny",
+        "SECTION_HEADER_PERMISSIONS"         : "@:MANAGE_USER.SECTION_HEADER_PERMISSIONS",
+        "SECTION_HEADER_USER_GROUPS"         : "Rodičovské skupiny",
+
+        "TEXT_CONFIRM_DELETE" : "Skupiny nelze obnovit po jejich odstranění. Opravdu chcete tuto skupinu smazat?"
+
+    },
+
+    "PROTOCOL_RDP" : {
+
+        "FIELD_HEADER_CLIENT_NAME"                : "Jméno klienta:",
+        "FIELD_HEADER_COLOR_DEPTH"                : "Barevná hloubka:",
+        "FIELD_HEADER_CONSOLE"                    : "Konzola pro správu:",
+        "FIELD_HEADER_CONSOLE_AUDIO"              : "Podpora zvuku v konzole:",
+        "FIELD_HEADER_CREATE_DRIVE_PATH"          : "Automaticky vytvořit disk:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH"      : "Automaticky vytvořit cestu k záznamu:",
+        "FIELD_HEADER_DISABLE_AUDIO"              : "Zakázat zvuk:",
+        "FIELD_HEADER_DISABLE_AUTH"               : "Zakázat ověřování:",
+        "FIELD_HEADER_DISABLE_COPY"               : "Zakázat kopírování ze vzdálené plochy:",
+        "FIELD_HEADER_DISABLE_PASTE"              : "Zakázat vkládání z klienta:",
+        "FIELD_HEADER_DOMAIN"                     : "Doména:",
+        "FIELD_HEADER_DPI"                        : "Rozlišení (DPI):",
+        "FIELD_HEADER_DRIVE_NAME"                 : "Název jednotky:",
+        "FIELD_HEADER_DRIVE_PATH"                 : "Cesta na disku:",
+        "FIELD_HEADER_ENABLE_AUDIO_INPUT"         : "Povolit zvukový vstup (mikrofon):",
+        "FIELD_HEADER_ENABLE_DESKTOP_COMPOSITION" : "Povolit kompozici pracovní plochy (Aero):",
+        "FIELD_HEADER_ENABLE_DRIVE"               : "Povolit jednotku:",
+        "FIELD_HEADER_ENABLE_FONT_SMOOTHING"      : "Povolit vyhlazení písma (ClearType):",
+        "FIELD_HEADER_ENABLE_FULL_WINDOW_DRAG"    : "Povolit přetažení celého okna:",
+        "FIELD_HEADER_ENABLE_MENU_ANIMATIONS"     : "Povolit animace nabídky:",
+        "FIELD_HEADER_DISABLE_BITMAP_CACHING"     : "Zakázat ukládání do mezipaměti bitmap:",
+        "FIELD_HEADER_DISABLE_OFFSCREEN_CACHING"  : "Zakázat ukládání do mezipaměti mimo obrazovku:",
+        "FIELD_HEADER_DISABLE_GLYPH_CACHING"      : "Zakázat ukládání do mezipaměti glyfů:",
+        "FIELD_HEADER_ENABLE_PRINTING"            : "Povolit tisk:",
+        "FIELD_HEADER_ENABLE_SFTP"                : "Povolit SFTP:",
+        "FIELD_HEADER_ENABLE_THEMING"             : "Povolit motivy:",
+        "FIELD_HEADER_ENABLE_WALLPAPER"           : "Povolit tapetu:",
+        "FIELD_HEADER_GATEWAY_DOMAIN"             : "Doména:",
+        "FIELD_HEADER_GATEWAY_HOSTNAME"           : "Jméno hostitele:",
+        "FIELD_HEADER_GATEWAY_PASSWORD"           : "Heslo:",
+        "FIELD_HEADER_GATEWAY_PORT"               : "Port:",
+        "FIELD_HEADER_GATEWAY_USERNAME"           : "Uživatelské jméno:",
+        "FIELD_HEADER_HEIGHT"                     : "Výška:",
+        "FIELD_HEADER_HOSTNAME"                   : "Jméno hostitele:",
+        "FIELD_HEADER_IGNORE_CERT"                : "Ignorovat serverový certifikát:",
+        "FIELD_HEADER_INITIAL_PROGRAM"            : "Úvodní program:",
+        "FIELD_HEADER_LOAD_BALANCE_INFO"          : "Vyvážení zátěže info/cookie:",
+        "FIELD_HEADER_PASSWORD"                   : "Heslo:",
+        "FIELD_HEADER_PORT"                       : "Port:",
+        "FIELD_HEADER_PRINTER_NAME"               : "Název přesměrované tiskárny:",
+        "FIELD_HEADER_PRECONNECTION_BLOB"         : "Preconnection BLOB (VM ID):",
+        "FIELD_HEADER_PRECONNECTION_ID"           : "Zdrojové ID RDP",
+        "FIELD_HEADER_READ_ONLY"                  : "Pouze ke čtení:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"    : "Vyloučit myš:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT"   : "Vyloučit grafiku/strímování:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"     : "Zahrnout klíčové události:",
+        "FIELD_HEADER_RECORDING_NAME"             : "Nahrávané jméno:",
+        "FIELD_HEADER_RECORDING_PATH"             : "Nahrávaná cesta:",
+        "FIELD_HEADER_RESIZE_METHOD"              : "Metoda změny velikosti:",
+        "FIELD_HEADER_REMOTE_APP_ARGS"            : "Parametry:",
+        "FIELD_HEADER_REMOTE_APP_DIR"             : "Pracovní adresář:",
+        "FIELD_HEADER_REMOTE_APP"                 : "Program:",
+        "FIELD_HEADER_SECURITY"                   : "Bezpečnostní mód:",
+        "FIELD_HEADER_SERVER_LAYOUT"              : "Rozložení klávesnice:",
+        "FIELD_HEADER_SFTP_DIRECTORY"             : "Výchozí složka pro nahrávání:",
+        "FIELD_HEADER_SFTP_HOST_KEY"              : "Veřejný klíč hosta (Base64)",
+        "FIELD_HEADER_SFTP_HOSTNAME"              : "Jméno hostitele:",
+        "FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL" : "SFTP keepalive interval:",
+        "FIELD_HEADER_SFTP_PASSPHRASE"            : "Přístupová fráze:",
+        "FIELD_HEADER_SFTP_PASSWORD"              : "Heslo:",
+        "FIELD_HEADER_SFTP_PORT"                  : "Port:",
+        "FIELD_HEADER_SFTP_PRIVATE_KEY"           : "Privátní klíč:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"        : "Souborový prohlížeč kořenové složky:",
+        "FIELD_HEADER_SFTP_USERNAME"              : "Uživatelské jméno:",
+        "FIELD_HEADER_STATIC_CHANNELS"            : "Názvy statických kanálů:",
+        "FIELD_HEADER_USERNAME"                   : "Uživatelské jméno:",
+        "FIELD_HEADER_WIDTH"                      : "Šířka:",
+
+        "FIELD_OPTION_COLOR_DEPTH_16"    : "Nízké barvy (16-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_24"    : "Opravdové barvy (24-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_32"    : "Opravdové barvy (32-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_8"     : "256 barev",
+        "FIELD_OPTION_COLOR_DEPTH_EMPTY" : "",
+
+        "FIELD_OPTION_RESIZE_METHOD_DISPLAY_UPDATE" : "Virtuální kanál „Aktualizace zobrazení“ (RDP 8.1+)",
+        "FIELD_OPTION_RESIZE_METHOD_EMPTY"     : "",
+        "FIELD_OPTION_RESIZE_METHOD_RECONNECT" : "Znovu připojit",
+
+        "FIELD_OPTION_SECURITY_ANY"   : "Jakýkoliv",
+        "FIELD_OPTION_SECURITY_EMPTY" : "",
+        "FIELD_OPTION_SECURITY_NLA"   : "NLA (Ověřování na úrovni sítě)",
+        "FIELD_OPTION_SECURITY_RDP"   : "Šifrování RDP",
+        "FIELD_OPTION_SECURITY_TLS"   : "Šifrování TLS",
+
+        "FIELD_OPTION_SERVER_LAYOUT_DE_DE_QWERTZ" : "Němčina (Qwertz)",
+        "FIELD_OPTION_SERVER_LAYOUT_EMPTY"        : "",
+        "FIELD_OPTION_SERVER_LAYOUT_EN_GB_QWERTY" : "UK Angličtina (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_EN_US_QWERTY" : "US Angličtina (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_ES_ES_QWERTY" : "Španělština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_FAILSAFE"     : "Unicode",
+        "FIELD_OPTION_SERVER_LAYOUT_FR_CH_QWERTZ" : "Švícarská Francouzština (Qwertz)",
+        "FIELD_OPTION_SERVER_LAYOUT_FR_FR_AZERTY" : "Francouzština (Azerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_IT_IT_QWERTY" : "Italština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_JA_JP_QWERTY" : "Japonština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_PT_BR_QWERTY" : "Portugalská Brazilština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_SV_SE_QWERTY" : "Švédština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_DA_DK_QWERTY" : "Dánština (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_TR_TR_QWERTY" : "Turečtina (Qwerty)",
+
+        "NAME" : "RDP",
+
+        "SECTION_HEADER_AUTHENTICATION"     : "Ověřování",
+        "SECTION_HEADER_BASIC_PARAMETERS"   : "Základní nastavení",
+        "SECTION_HEADER_CLIPBOARD"          : "Schránka",
+        "SECTION_HEADER_DEVICE_REDIRECTION" : "Přesměrování zařízení",
+        "SECTION_HEADER_DISPLAY"            : "Zobrazení",
+        "SECTION_HEADER_GATEWAY"            : "Brána vzdálené plochy",
+        "SECTION_HEADER_LOAD_BALANCING"     : "Rozložení zátěže",
+        "SECTION_HEADER_NETWORK"            : "Síť",
+        "SECTION_HEADER_PERFORMANCE"        : "Výkon",
+        "SECTION_HEADER_PRECONNECTION_PDU"  : "Preconnection PDU / Hyper-V",
+        "SECTION_HEADER_RECORDING"          : "Nahrávání obrazovky",
+        "SECTION_HEADER_REMOTEAPP"          : "RemoteApp",
+        "SECTION_HEADER_SFTP"               : "SFTP"
+
+    },
+
+    "PROTOCOL_SSH" : {
+
+        "FIELD_HEADER_BACKSPACE"                : "Backspace, poslat klávesy:",
+        "FIELD_HEADER_COLOR_SCHEME"             : "Barva",
+        "FIELD_HEADER_COMMAND"                  : "Provést příkaz:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH"    : "Automaticky vytvořit cestu k nahrávání:",
+        "FIELD_HEADER_CREATE_TYPESCRIPT_PATH"   : "Automaticky vytvořit cestu ke strojopisu:",
+        "FIELD_HEADER_DISABLE_COPY"             : "Zakázat kopírování ze vzdáleného terminálu:",
+        "FIELD_HEADER_DISABLE_PASTE"            : "Zakázat vkládání z klienta:",
+        "FIELD_HEADER_FONT_NAME"                : "Typ fontu",
+        "FIELD_HEADER_FONT_SIZE"                : "Velikost písma:",
+        "FIELD_HEADER_ENABLE_SFTP"              : "Povolit SFTP:",
+        "FIELD_HEADER_HOST_KEY"                 : "Veřejný klíč hosta (Base64):",
+        "FIELD_HEADER_HOSTNAME"                 : "Jméno hostitele:",
+        "FIELD_HEADER_USERNAME"                 : "Uživatelské jméno:",
+        "FIELD_HEADER_PASSWORD"                 : "Heslo:",
+        "FIELD_HEADER_PASSPHRASE"               : "Přístupová fráze:",
+        "FIELD_HEADER_PORT"                     : "Port:",
+        "FIELD_HEADER_PRIVATE_KEY"              : "Privátní klíč:",
+        "FIELD_HEADER_READ_ONLY"                : "Pouze ke čtení:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "Vynechat myš:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "Vynechat grafiku/streamování:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "Zahrnout události kláves",
+        "FIELD_HEADER_RECORDING_NAME"           : "Nahrávané jméno:",
+        "FIELD_HEADER_RECORDING_PATH"           : "Nahrávaná cesta:",
+        "FIELD_HEADER_SERVER_ALIVE_INTERVAL"    : "Serverový keepalive interval:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"      : "Souborový prohlížeč kořenové složky:",
+        "FIELD_HEADER_TERMINAL_TYPE"            : "Typ terminálu:",
+        "FIELD_HEADER_TYPESCRIPT_NAME"          : "Jméno strojopisu:",
+        "FIELD_HEADER_TYPESCRIPT_PATH"          : "Cesta ke strojopisu:",
+
+        "FIELD_OPTION_BACKSPACE_EMPTY"          : "",
+        "FIELD_OPTION_BACKSPACE_8"              : "Backspace (Ctrl-H)",
+        "FIELD_OPTION_BACKSPACE_127"            : "Delete (Ctrl-?)",
+        "FIELD_OPTION_COLOR_SCHEME_BLACK_WHITE" : "Černá na bílé",
+        "FIELD_OPTION_COLOR_SCHEME_EMPTY"       : "",
+        "FIELD_OPTION_COLOR_SCHEME_GRAY_BLACK"  : "Šedá na černé",
+        "FIELD_OPTION_COLOR_SCHEME_GREEN_BLACK" : "Zelená na černé",
+        "FIELD_OPTION_COLOR_SCHEME_WHITE_BLACK" : "Bílá na černé",
+
+        "FIELD_OPTION_FONT_SIZE_8"     : "8",
+        "FIELD_OPTION_FONT_SIZE_9"     : "9",
+        "FIELD_OPTION_FONT_SIZE_10"    : "10",
+        "FIELD_OPTION_FONT_SIZE_11"    : "11",
+        "FIELD_OPTION_FONT_SIZE_12"    : "12",
+        "FIELD_OPTION_FONT_SIZE_14"    : "14",
+        "FIELD_OPTION_FONT_SIZE_18"    : "18",
+        "FIELD_OPTION_FONT_SIZE_24"    : "24",
+        "FIELD_OPTION_FONT_SIZE_30"    : "30",
+        "FIELD_OPTION_FONT_SIZE_36"    : "36",
+        "FIELD_OPTION_FONT_SIZE_48"    : "48",
+        "FIELD_OPTION_FONT_SIZE_60"    : "60",
+        "FIELD_OPTION_FONT_SIZE_72"    : "72",
+        "FIELD_OPTION_FONT_SIZE_96"    : "96",
+        "FIELD_OPTION_FONT_SIZE_EMPTY" : "",
+
+        "FIELD_OPTION_TERMINAL_TYPE_ANSI"           : "ansi",
+        "FIELD_OPTION_TERMINAL_TYPE_EMPTY"          : "",
+        "FIELD_OPTION_TERMINAL_TYPE_LINUX"          : "linux",
+        "FIELD_OPTION_TERMINAL_TYPE_VT100"          : "vt100",
+        "FIELD_OPTION_TERMINAL_TYPE_VT220"          : "vt220",
+        "FIELD_OPTION_TERMINAL_TYPE_XTERM"          : "xterm",
+        "FIELD_OPTION_TERMINAL_TYPE_XTERM_256COLOR" : "xterm-256color",
+
+        "NAME" : "SSH",
+
+        "SECTION_HEADER_AUTHENTICATION" : "Ověřování",
+        "SECTION_HEADER_BEHAVIOR"       : "Chování terminálu",
+        "SECTION_HEADER_CLIPBOARD"      : "Schránka",
+        "SECTION_HEADER_DISPLAY"        : "Zobrazení",
+        "SECTION_HEADER_NETWORK"        : "Síť",
+        "SECTION_HEADER_RECORDING"      : "Nahrávání obrazovky",
+        "SECTION_HEADER_SESSION"        : "Sezení / prostředí",
+        "SECTION_HEADER_TYPESCRIPT"     : "Strojopis (textové nahrávání sezení)",
+        "SECTION_HEADER_SFTP"           : "SFTP"
+
+    },
+
+    "PROTOCOL_TELNET" : {
+
+        "FIELD_HEADER_BACKSPACE"                : "Backspace, poslat klávesy:",
+        "FIELD_HEADER_COLOR_SCHEME"             : "Barevné schéma:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH"    : "Automaticky vytvořit cestu k nahrávání:",
+        "FIELD_HEADER_CREATE_TYPESCRIPT_PATH"   : "Automaticky vytvořit cestu k typescriptu:",
+        "FIELD_HEADER_DISABLE_COPY"             : "Zakázat kopírování z terminálu:",
+        "FIELD_HEADER_DISABLE_PASTE"            : "Zakázat vkládání z klienta:",
+        "FIELD_HEADER_FONT_NAME"                : "Jméno fontu:",
+        "FIELD_HEADER_FONT_SIZE"                : "Velikost fontu:",
+        "FIELD_HEADER_HOSTNAME"                 : "Jméno hostitele:",
+        "FIELD_HEADER_LOGIN_FAILURE_REGEX"      : "Selhání přihlášení regulární výraz:",
+        "FIELD_HEADER_LOGIN_SUCCESS_REGEX"      : "Úspěch přihlášení regulární výraz:",
+        "FIELD_HEADER_USERNAME"                 : "Uživatelské jméno:",
+        "FIELD_HEADER_USERNAME_REGEX"           : "Uživatelské jméno regulární výraz:",
+        "FIELD_HEADER_PASSWORD"                 : "Heslo:",
+        "FIELD_HEADER_PASSWORD_REGEX"           : "Heslo regulární výraz:",
+        "FIELD_HEADER_PORT"                     : "Port:",
+        "FIELD_HEADER_READ_ONLY"                : "Pouze ke čtení:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "Vyloučit myš:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "Vyloučit grafiku/strímování:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "Zahrnout klíčové události:",
+        "FIELD_HEADER_RECORDING_NAME"           : "Nahrávané jméno:",
+        "FIELD_HEADER_RECORDING_PATH"           : "Nahrávaná cesta:",
+        "FIELD_HEADER_TERMINAL_TYPE"            : "Typ terminálu:",
+        "FIELD_HEADER_TYPESCRIPT_NAME"          : "Jméno strojopisu:",
+        "FIELD_HEADER_TYPESCRIPT_PATH"          : "Cesta ke strojopisu:",
+
+        "FIELD_OPTION_BACKSPACE_EMPTY" : "",
+        "FIELD_OPTION_BACKSPACE_8"     : "Backspace (Ctrl-H)",
+        "FIELD_OPTION_BACKSPACE_127"   : "Delete (Ctrl-?)",
+
+        "FIELD_OPTION_COLOR_SCHEME_BLACK_WHITE" : "Černá na bílé",
+        "FIELD_OPTION_COLOR_SCHEME_EMPTY"       : "",
+        "FIELD_OPTION_COLOR_SCHEME_GRAY_BLACK"  : "Šedá na černé",
+        "FIELD_OPTION_COLOR_SCHEME_GREEN_BLACK" : "Zelená na černé",
+        "FIELD_OPTION_COLOR_SCHEME_WHITE_BLACK" : "Bílá na černé",
+
+        "FIELD_OPTION_FONT_SIZE_8"     : "8",
+        "FIELD_OPTION_FONT_SIZE_9"     : "9",
+        "FIELD_OPTION_FONT_SIZE_10"    : "10",
+        "FIELD_OPTION_FONT_SIZE_11"    : "11",
+        "FIELD_OPTION_FONT_SIZE_12"    : "12",
+        "FIELD_OPTION_FONT_SIZE_14"    : "14",
+        "FIELD_OPTION_FONT_SIZE_18"    : "18",
+        "FIELD_OPTION_FONT_SIZE_24"    : "24",
+        "FIELD_OPTION_FONT_SIZE_30"    : "30",
+        "FIELD_OPTION_FONT_SIZE_36"    : "36",
+        "FIELD_OPTION_FONT_SIZE_48"    : "48",
+        "FIELD_OPTION_FONT_SIZE_60"    : "60",
+        "FIELD_OPTION_FONT_SIZE_72"    : "72",
+        "FIELD_OPTION_FONT_SIZE_96"    : "96",
+        "FIELD_OPTION_FONT_SIZE_EMPTY" : "",
+
+        "FIELD_OPTION_TERMINAL_TYPE_ANSI"           : "ansi",
+        "FIELD_OPTION_TERMINAL_TYPE_EMPTY"          : "",
+        "FIELD_OPTION_TERMINAL_TYPE_LINUX"          : "linux",
+        "FIELD_OPTION_TERMINAL_TYPE_VT100"          : "vt100",
+        "FIELD_OPTION_TERMINAL_TYPE_VT220"          : "vt220",
+        "FIELD_OPTION_TERMINAL_TYPE_XTERM"          : "xterm",
+        "FIELD_OPTION_TERMINAL_TYPE_XTERM_256COLOR" : "xterm-256color",
+
+        "NAME" : "Telnet",
+
+        "SECTION_HEADER_AUTHENTICATION" : "Ověřování",
+        "SECTION_HEADER_BEHAVIOR"       : "Chování terminálu",
+        "SECTION_HEADER_CLIPBOARD"      : "Schránka",
+        "SECTION_HEADER_DISPLAY"        : "Zobrazení",
+        "SECTION_HEADER_RECORDING"      : "Nahrávání obrazovky",
+        "SECTION_HEADER_TYPESCRIPT"     : "Strojopis (textové nahrávání sezení)",
+        "SECTION_HEADER_NETWORK"        : "Síť"
+
+    },
+
+    "PROTOCOL_VNC" : {
+
+        "FIELD_HEADER_AUDIO_SERVERNAME"           : "Název zvukového serveru",
+        "FIELD_HEADER_CLIPBOARD_ENCODING"         : "Kódovávání:",
+        "FIELD_HEADER_COLOR_DEPTH"                : "Hloubka barev:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH"      : "Automaticky vytvořit cestu k nahrávání:",
+        "FIELD_HEADER_CURSOR"                     : "Kurzor:",
+        "FIELD_HEADER_DEST_HOST"                  : "Cílový host:",
+        "FIELD_HEADER_DEST_PORT"                  : "Vzdálený port:",
+        "FIELD_HEADER_DISABLE_COPY"               : "Zakázat kopírování ze vzdálené plochy:",
+        "FIELD_HEADER_DISABLE_PASTE"              : "Zakázat vkládání z klienta:",
+        "FIELD_HEADER_ENABLE_AUDIO"               : "Zapnout audio",
+        "FIELD_HEADER_ENABLE_SFTP"                : "Povolit SFTP:",
+        "FIELD_HEADER_HOSTNAME"                   : "Jméno hostitele:",
+        "FIELD_HEADER_USERNAME"                   : "Uživatelské jméno:",
+        "FIELD_HEADER_PASSWORD"                   : "Heslo:",
+        "FIELD_HEADER_PORT"                       : "Port:",
+        "FIELD_HEADER_READ_ONLY"                  : "Pouze čtení:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"    : "Vynechat myš:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT"   : "Vynechat grafiku/stremování:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"     : "Zahrnout události kláves",
+        "FIELD_HEADER_RECORDING_NAME"             : "Nahrávané jméno:",
+        "FIELD_HEADER_RECORDING_PATH"             : "Nahrávaná cesta:",
+        "FIELD_HEADER_SFTP_DIRECTORY"             : "Výchozí složka pro nahrávání:",
+        "FIELD_HEADER_SFTP_HOST_KEY"              : "Veřejný klíč hosta (Base64)",
+        "FIELD_HEADER_SFTP_HOSTNAME"              : "Jméno hostitele:",
+        "FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL" : "SFTP keepalive interval:",
+        "FIELD_HEADER_SFTP_PASSPHRASE"            : "Přístupová fráze:",
+        "FIELD_HEADER_SFTP_PASSWORD"              : "Heslo:",
+        "FIELD_HEADER_SFTP_PORT"                  : "Port:",
+        "FIELD_HEADER_SFTP_PRIVATE_KEY"           : "Privátní klíč:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"        : "Souborový prohlížeč kořenové složky:",
+        "FIELD_HEADER_SFTP_USERNAME"              : "Uživatelské jméno:",
+        "FIELD_HEADER_SWAP_RED_BLUE"              : "Přehodit červené/modré komponenty:",
+
+        "FIELD_OPTION_COLOR_DEPTH_8"     : "256 barev",
+        "FIELD_OPTION_COLOR_DEPTH_16"    : "Nízké barvy (16-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_24"    : "Opravdové barvy (24-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_32"    : "Opravdové barvy (32-bitů)",
+        "FIELD_OPTION_COLOR_DEPTH_EMPTY" : "",
+
+        "FIELD_OPTION_CURSOR_EMPTY"  : "",
+        "FIELD_OPTION_CURSOR_LOCAL"  : "Místní",
+        "FIELD_OPTION_CURSOR_REMOTE" : "Vzdálený",
+
+        "FIELD_OPTION_CLIPBOARD_ENCODING_CP1252"    : "CP1252",
+        "FIELD_OPTION_CLIPBOARD_ENCODING_EMPTY"     : "",
+        "FIELD_OPTION_CLIPBOARD_ENCODING_ISO8859_1" : "ISO 8859-1",
+        "FIELD_OPTION_CLIPBOARD_ENCODING_UTF_8"     : "UTF-8",
+        "FIELD_OPTION_CLIPBOARD_ENCODING_UTF_16"    : "UTF-16",
+
+        "NAME" : "VNC",
+
+        "SECTION_HEADER_AUDIO"          : "Zvuk",
+        "SECTION_HEADER_AUTHENTICATION" : "Ověřování",
+        "SECTION_HEADER_CLIPBOARD"      : "Schránka",
+        "SECTION_HEADER_DISPLAY"        : "Zobrazení",
+        "SECTION_HEADER_NETWORK"        : "Síť",
+        "SECTION_HEADER_RECORDING"      : "Nahrávání obrazovky",
+        "SECTION_HEADER_REPEATER"       : "VNC opakovač",
+        "SECTION_HEADER_SFTP"           : "SFTP"
+
+    },
+
+    "SETTINGS" : {
+
+        "SECTION_HEADER_SETTINGS" : "Nastavení"
+
+    },
+
+    "SETTINGS_CONNECTION_HISTORY" : {
+
+        "ACTION_DOWNLOAD" : "@:APP.ACTION_DOWNLOAD",
+        "ACTION_SEARCH"   : "@:APP.ACTION_SEARCH",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FILENAME_HISTORY_CSV" : "history.csv",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_CONNECTION_HISTORY" : "Zde jsou uvedeny záznamy o historii připojení a lze je třídit kliknutím na záhlaví sloupců. Chcete-li vyhledat konkrétní záznamy, zadejte řetězec filtrů a klikněte na tlačítko Hledat. Zobrazí se pouze záznamy, které odpovídají zadanému řetězci filtrů.",
+
+        "INFO_CONNECTION_DURATION_UNKNOWN" : "--",
+        "INFO_NO_HISTORY"                  : "Žádné shodné záznamy",
+
+        "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Název připojení",
+        "TABLE_HEADER_SESSION_DURATION"        : "Doba trvání",
+        "TABLE_HEADER_SESSION_REMOTEHOST"      : "Vzdálený host",
+        "TABLE_HEADER_SESSION_STARTDATE"       : "Počáteční čas",
+        "TABLE_HEADER_SESSION_USERNAME"        : "Uživatelské jméno",
+
+        "TEXT_HISTORY_DURATION" : "@:APP.TEXT_HISTORY_DURATION"
+
+    },
+
+    "SETTINGS_CONNECTIONS" : {
+
+        "ACTION_ACKNOWLEDGE"          : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_CONNECTION"       : "Nové připojení",
+        "ACTION_NEW_CONNECTION_GROUP" : "Nový skupina",
+        "ACTION_NEW_SHARING_PROFILE"  : "Nový sdílený profil",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_CONNECTIONS" : "Toto připojení můžete spravovat klepnutím nebo klepnutím na níže uvedené připojení. V závislosti na vaší úrovni přístupu lze přidávat a mazat připojení a měnit jejich vlastnosti (protokol, název hostitele, port atd.).",
+
+        "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
+
+        "SECTION_HEADER_CONNECTIONS" : "Připojení"
+
+    },
+
+    "SETTINGS_PREFERENCES" : {
+
+        "ACTION_ACKNOWLEDGE"     : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"          : "@:APP.ACTION_CANCEL",
+        "ACTION_UPDATE_PASSWORD" : "@:APP.ACTION_UPDATE_PASSWORD",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_PASSWORD_BLANK"    : "@:APP.ERROR_PASSWORD_BLANK",
+        "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
+
+        "FIELD_HEADER_LANGUAGE"           : "Jazyk zobrazení:",
+        "FIELD_HEADER_PASSWORD"           : "Heslo:",
+        "FIELD_HEADER_PASSWORD_OLD"       : "Aktuální heslo:",
+        "FIELD_HEADER_PASSWORD_NEW"       : "Nové heslo:",
+        "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "Potvrďte nové heslo:",
+        "FIELD_HEADER_USERNAME"           : "Uživatelské jméno:",
+
+        "HELP_DEFAULT_INPUT_METHOD" : "Výchozí metoda vstupu určuje, jak Guacamole přijímá události klávesnice. Změna tohoto nastavení může být nezbytná při používání mobilního zařízení nebo při psaní přes IME. Toto nastavení může být přepsáno na základě připojení v rámci nabídky Guacamole.",
+        "HELP_DEFAULT_MOUSE_MODE"   : "Výchozí režim emulace myši určuje, jak se bude vzdálená myš chovat v nových spojeních s ohledem na dotyky. Toto nastavení může být přepsáno na základě připojení v rámci nabídky Guacamole.",
+        "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"             : "Chcete-li změnit jazyk celého textu v Guacamole, vyberte níže uvedený jazyk. Dostupné volby budou záviset na nainstalovaných jazycích.",
+        "HELP_MOUSE_MODE_ABSOLUTE"  : "@:CLIENT.HELP_MOUSE_MODE_ABSOLUTE",
+        "HELP_MOUSE_MODE_RELATIVE"  : "@:CLIENT.HELP_MOUSE_MODE_RELATIVE",
+        "HELP_UPDATE_PASSWORD"      : "Pokud chcete změnit heslo, zadejte své aktuální heslo a níže požadované nové heslo a klikněte na tlačítko „Aktualizovat heslo“. Změna se projeví okamžitě.",
+
+        "INFO_PASSWORD_CHANGED" : "Heslo bylo změněno.",
+
+        "NAME_INPUT_METHOD_NONE" : "@:CLIENT.NAME_INPUT_METHOD_NONE",
+        "NAME_INPUT_METHOD_OSK"  : "@:CLIENT.NAME_INPUT_METHOD_OSK",
+        "NAME_INPUT_METHOD_TEXT" : "@:CLIENT.NAME_INPUT_METHOD_TEXT",
+
+        "SECTION_HEADER_DEFAULT_INPUT_METHOD" : "Výchozí metoda vstupu",
+        "SECTION_HEADER_DEFAULT_MOUSE_MODE"   : "Výchozí mód emulace myši",
+        "SECTION_HEADER_UPDATE_PASSWORD"      : "Změnit heslo"
+
+    },
+
+    "SETTINGS_USERS" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_USER"    : "Nový uživatel",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_USERS" : "Chcete-li spravovat daného uživatele, klepněte na něj nebo klepněte na něj. V závislosti na úrovni přístupu mohou být uživatelé přidáváni a mazáni a jejich hesla mohou být změněna.",
+
+        "SECTION_HEADER_USERS" : "Uživatel",
+
+        "TABLE_HEADER_FULL_NAME"    : "Celé jméno",
+        "TABLE_HEADER_LAST_ACTIVE"  : "Poslední aktivní",
+        "TABLE_HEADER_ORGANIZATION" : "Organizace",
+        "TABLE_HEADER_USERNAME"     : "Uživatelské jméno"
+
+    },
+
+    "SETTINGS_USER_GROUPS" : {
+
+        "ACTION_ACKNOWLEDGE"    : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_USER_GROUP" : "Nová skupina",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_USER_GROUPS" : "Chcete-li tuto skupinu spravovat, klepněte na ni nebo klepněte na ni. V závislosti na úrovni přístupu lze skupiny přidávat a mazat a jejich členské uživatele a skupiny lze měnit.",
+
+        "SECTION_HEADER_USER_GROUPS" : "Skupiny",
+
+        "TABLE_HEADER_USER_GROUP_NAME" : "Jméno skupiny"
+
+    },
+
+    "SETTINGS_SESSIONS" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_DELETE"      : "Ukončit sezení",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "Ukončit sezení",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_SESSIONS" : "Tato stránka bude naplněna aktuálně aktivními připojeními. Uvedená připojení a schopnost zabít tato připojení závisí na úrovni přístupu. Pokud chcete zabít jednu nebo více relací, zaškrtněte políčko vedle těchto relací a klepněte na tlačítko \"Zabít relace\". Zabití relace okamžitě odpojí uživatele od přidruženého připojení.",
+
+        "INFO_NO_SESSIONS" : "Žádné aktivní sezení",
+
+        "SECTION_HEADER_SESSIONS" : "Aktivní sezení",
+
+        "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Název připojení",
+        "TABLE_HEADER_SESSION_REMOTEHOST"      : "Vzdálený host",
+        "TABLE_HEADER_SESSION_STARTDATE"       : "Aktivní od",
+        "TABLE_HEADER_SESSION_USERNAME"        : "Uživatelské jméno:",
+
+        "TEXT_CONFIRM_DELETE" : "Jste si jisti, že chcete ukončit vybrané sezení? Uživatele užívající toto spojení budou okamžitě odpojeni. "
+
+    },
+
+    "USER_ATTRIBUTES" : {
+
+        "FIELD_HEADER_GUAC_EMAIL_ADDRESS"       : "Emailová adresa:",
+        "FIELD_HEADER_GUAC_FULL_NAME"           : "Celé jméno:",
+        "FIELD_HEADER_GUAC_ORGANIZATION"        : "Organizace:",
+        "FIELD_HEADER_GUAC_ORGANIZATIONAL_ROLE" : "Role:"
+
+    },
+
+    "USER_MENU" : {
+
+        "ACTION_LOGOUT"             : "@:APP.ACTION_LOGOUT",
+        "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS",
+        "ACTION_MANAGE_PREFERENCES" : "@:APP.ACTION_MANAGE_PREFERENCES",
+        "ACTION_MANAGE_SESSIONS"    : "@:APP.ACTION_MANAGE_SESSIONS",
+        "ACTION_MANAGE_SETTINGS"    : "@:APP.ACTION_MANAGE_SETTINGS",
+        "ACTION_MANAGE_USERS"       : "@:APP.ACTION_MANAGE_USERS",
+        "ACTION_MANAGE_USER_GROUPS" : "@:APP.ACTION_MANAGE_USER_GROUPS",
+        "ACTION_NAVIGATE_HOME"      : "@:APP.ACTION_NAVIGATE_HOME",
+        "ACTION_VIEW_HISTORY"       : "@:APP.ACTION_VIEW_HISTORY"
+
+    }
+
+}
diff --git a/guacamole/src/main/webapp/translations/de.json b/guacamole/src/main/webapp/translations/de.json
index 9e24ba7..c62edef 100644
--- a/guacamole/src/main/webapp/translations/de.json
+++ b/guacamole/src/main/webapp/translations/de.json
@@ -91,6 +91,8 @@
         "ERROR_UPLOAD_31D"     : "Die maximale Anzahl gleichzeiter Dateiübertragungen erreicht. Bitte warte bis laufende Dateiübertagungen abgeschlossen sind und versuche es erneut.",
         "ERROR_UPLOAD_DEFAULT" : "Die Verbindung wurde aufgrund eines interen Fehlers im Guacamole Server beendet. Sollte dieses Problem weiterhin bestehen informieren Sie den Systemadministrator oder überprüfen Sie die Protokolle.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Kopierter oder ausgeschnittener Text aus Guacamole wird hier angezeigt. Änderungen am Text werden direkt auf die entfernte Zwischenablage angewandt.",
         "HELP_INPUT_METHOD_NONE"   : "Keine Eingabemethode in Verwendung. Tastatureingaben werden von der Hardwaretastatur akzeptiert.",
         "HELP_INPUT_METHOD_OSK"    : "Bildschirmeingaben und die eingebettete Guacamole Bildschrimtastatur werden akzeptiert. Die Bildschirmtastatur gestattet Tastenkombinationen die ansonsten unmöglich sind (z.B.: Strg-Alt-Del).",
@@ -354,6 +356,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Authentifizierung",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Basiseinstellungen",
+        "SECTION_HEADER_CLIPBOARD"          : "Zwischenablage",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Geräteumleitung",
         "SECTION_HEADER_DISPLAY"            : "Bildschirm",
         "SECTION_HEADER_NETWORK"            : "Netzwerk",
@@ -403,6 +406,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentifizierung",
+        "SECTION_HEADER_CLIPBOARD"      : "Zwischenablage",
         "SECTION_HEADER_DISPLAY"        : "Bildschirm",
         "SECTION_HEADER_NETWORK"        : "Netzwerk",
         "SECTION_HEADER_SESSION"        : "Sitzung / Umgebung",
@@ -447,6 +451,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentifizierung",
+        "SECTION_HEADER_CLIPBOARD"      : "Zwischenablage",
         "SECTION_HEADER_DISPLAY"        : "Bildschirm",
         "SECTION_HEADER_NETWORK"        : "Netzwerk"
 
@@ -463,6 +468,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Aktiviere Audio:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Aktiviere SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Hostname:",
+        "FIELD_HEADER_USERNAME"         : "Benutzername:",
         "FIELD_HEADER_PASSWORD"         : "Passwort:",
         "FIELD_HEADER_PORT"             : "Port:",
         "FIELD_HEADER_READ_ONLY"        : "Nur-Lesen:",
diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json
index 7b29549..63deae5 100644
--- a/guacamole/src/main/webapp/translations/en.json
+++ b/guacamole/src/main/webapp/translations/en.json
@@ -106,6 +106,8 @@
         "ERROR_UPLOAD_31D"     : "Too many files are currently being transferred. Please wait for existing transfers to complete, and then try again.",
         "ERROR_UPLOAD_DEFAULT" : "An internal error has occurred within the Guacamole server, and the connection has been terminated. If the problem persists, please notify your system administrator, or check your system logs.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Text copied/cut within Guacamole will appear here. Changes to the text below will affect the remote clipboard.",
         "HELP_INPUT_METHOD_NONE"   : "No input method is used. Keyboard input is accepted from a connected, physical keyboard.",
         "HELP_INPUT_METHOD_OSK"    : "Display and accept input from the built-in Guacamole on-screen keyboard. The on-screen keyboard allows typing of key combinations that may otherwise be impossible (such as Ctrl-Alt-Del).",
@@ -455,6 +457,8 @@
         "FIELD_HEADER_CREATE_RECORDING_PATH" : "Automatically create recording path:",
         "FIELD_HEADER_DISABLE_AUDIO"   : "Disable audio:",
         "FIELD_HEADER_DISABLE_AUTH"    : "Disable authentication:",
+        "FIELD_HEADER_DISABLE_COPY"    : "Disable copying from remote desktop:",
+        "FIELD_HEADER_DISABLE_PASTE"   : "Disable pasting from client:",
         "FIELD_HEADER_DOMAIN"          : "Domain:",
         "FIELD_HEADER_DPI"             : "Resolution (DPI):",
         "FIELD_HEADER_DRIVE_NAME"      : "Drive name:",
@@ -539,6 +543,7 @@
         "FIELD_OPTION_SERVER_LAYOUT_FAILSAFE"     : "Unicode",
         "FIELD_OPTION_SERVER_LAYOUT_FR_CH_QWERTZ" : "Swiss French (Qwertz)",
         "FIELD_OPTION_SERVER_LAYOUT_FR_FR_AZERTY" : "French (Azerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_HU_HU_QWERTZ" : "Hungarian (Qwertz)",        
         "FIELD_OPTION_SERVER_LAYOUT_IT_IT_QWERTY" : "Italian (Qwerty)",
         "FIELD_OPTION_SERVER_LAYOUT_JA_JP_QWERTY" : "Japanese (Qwerty)",
         "FIELD_OPTION_SERVER_LAYOUT_PT_BR_QWERTY" : "Portuguese Brazilian (Qwerty)",
@@ -550,6 +555,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Authentication",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Basic Settings",
+        "SECTION_HEADER_CLIPBOARD"          : "Clipboard",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Device Redirection",
         "SECTION_HEADER_DISPLAY"            : "Display",
         "SECTION_HEADER_GATEWAY"            : "Remote Desktop Gateway",
@@ -570,6 +576,8 @@
         "FIELD_HEADER_COMMAND"      : "Execute command:",
         "FIELD_HEADER_CREATE_RECORDING_PATH" : "Automatically create recording path:",
         "FIELD_HEADER_CREATE_TYPESCRIPT_PATH" : "Automatically create typescript path:",
+        "FIELD_HEADER_DISABLE_COPY"  : "Disable copying from terminal:",
+        "FIELD_HEADER_DISABLE_PASTE" : "Disable pasting from client:",
         "FIELD_HEADER_FONT_NAME"     : "Font name:",
         "FIELD_HEADER_FONT_SIZE"     : "Font size:",
         "FIELD_HEADER_ENABLE_SFTP"   : "Enable SFTP:",
@@ -633,6 +641,7 @@
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentication",
         "SECTION_HEADER_BEHAVIOR"       : "Terminal behavior",
+        "SECTION_HEADER_CLIPBOARD"      : "Clipboard",
         "SECTION_HEADER_DISPLAY"        : "Display",
         "SECTION_HEADER_NETWORK"        : "Network",
         "SECTION_HEADER_RECORDING"      : "Screen Recording",
@@ -648,6 +657,8 @@
         "FIELD_HEADER_COLOR_SCHEME"   : "Color scheme:",
         "FIELD_HEADER_CREATE_RECORDING_PATH" : "Automatically create recording path:",
         "FIELD_HEADER_CREATE_TYPESCRIPT_PATH" : "Automatically create typescript path:",
+        "FIELD_HEADER_DISABLE_COPY"   : "Disable copying from terminal:",
+        "FIELD_HEADER_DISABLE_PASTE"  : "Disable pasting from client:",
         "FIELD_HEADER_FONT_NAME"      : "Font name:",
         "FIELD_HEADER_FONT_SIZE"      : "Font size:",
         "FIELD_HEADER_HOSTNAME"       : "Hostname:",
@@ -707,6 +718,7 @@
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentication",
         "SECTION_HEADER_BEHAVIOR"       : "Terminal behavior",
+        "SECTION_HEADER_CLIPBOARD"      : "Clipboard",
         "SECTION_HEADER_DISPLAY"        : "Display",
         "SECTION_HEADER_RECORDING"      : "Screen Recording",
         "SECTION_HEADER_TYPESCRIPT"     : "Typescript (Text Session Recording)",
@@ -723,9 +735,12 @@
         "FIELD_HEADER_CURSOR"           : "Cursor:",
         "FIELD_HEADER_DEST_HOST"        : "Destination host:",
         "FIELD_HEADER_DEST_PORT"        : "Destination port:",
+        "FIELD_HEADER_DISABLE_COPY"     : "Disable copying from remote desktop:",
+        "FIELD_HEADER_DISABLE_PASTE"    : "Disable pasting from client:",
         "FIELD_HEADER_ENABLE_AUDIO"     : "Enable audio:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Enable SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Hostname:",
+        "FIELD_HEADER_USERNAME"         : "Username:",
         "FIELD_HEADER_PASSWORD"         : "Password:",
         "FIELD_HEADER_PORT"             : "Port:",
         "FIELD_HEADER_READ_ONLY"        : "Read-only:",
diff --git a/guacamole/src/main/webapp/translations/es.json b/guacamole/src/main/webapp/translations/es.json
index c165d5c..a3dfc5a 100644
--- a/guacamole/src/main/webapp/translations/es.json
+++ b/guacamole/src/main/webapp/translations/es.json
@@ -4,9 +4,6 @@
     
     "APP" : {
 
-        "NAME"    : "Apache Guacamole",
-        "VERSION" : "0.9.13-incubating",
-
         "ACTION_ACKNOWLEDGE"        : "OK",
         "ACTION_CANCEL"             : "Cancelar",
         "ACTION_CLONE"              : "Clonar",
@@ -104,6 +101,8 @@
         "ERROR_UPLOAD_31D"     : "Se estan transfiriendo muchos ficheros actualmente. Por favor espere a que finalicen las transferencias de fichero existentes e intente de nuevo.",
         "ERROR_UPLOAD_DEFAULT" : "Ha ocurrido un error interno en el servidor Guacamole y la conexión ha finalizado. Si el problema persiste, por favor notifíquelo al administrador o compruebe los registros del sistema.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Aquí aparecerá el texto copiado/cortado en Guacamole. Los cambios en el texto de abajo afectaran al portapapeles remoto.",
         "HELP_INPUT_METHOD_NONE"   : "No se está usando un método de entrada. La entrada de teclado se acepta desde un teclado físico conectado.",
         "HELP_INPUT_METHOD_OSK"    : "Muestra y acepta entrada desde el teclado en pantalla incorporado de Guacamole. El teclado en pantalla permite escribir combinaciones que serían imposible de otro modo (como Ctrl-Alt-Sup).",
@@ -399,6 +398,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Autenticación",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Configuración básica",
+        "SECTION_HEADER_CLIPBOARD"          : "Portapapeles",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Redirección dispositivo",
         "SECTION_HEADER_DISPLAY"            : "Visualización",
         "SECTION_HEADER_GATEWAY"            : "Puerta de enlace remota",
@@ -460,6 +460,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Autenticación",
+        "SECTION_HEADER_CLIPBOARD"      : "Portapapeles",
         "SECTION_HEADER_DISPLAY"        : "Mostrar",
         "SECTION_HEADER_NETWORK"        : "Red",
         "SECTION_HEADER_RECORDING"      : "Grabación de pantalla",
@@ -512,6 +513,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Autenticación",
+        "SECTION_HEADER_CLIPBOARD"      : "Portapapeles",
         "SECTION_HEADER_DISPLAY"        : "Mostrar",
         "SECTION_HEADER_RECORDING"      : "Grabación pantalla",
         "SECTION_HEADER_TYPESCRIPT"     : "Script de escritura (Próxima sesión de grabación)",
@@ -531,6 +533,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Habilitar audio:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Habilitar SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Nombre de Host:",
+        "FIELD_HEADER_USERNAME"         : "Usuario:",
         "FIELD_HEADER_PASSWORD"         : "Contraseña:",
         "FIELD_HEADER_PORT"             : "Puerto:",
         "FIELD_HEADER_READ_ONLY"        : "Solo Lectura:",
diff --git a/guacamole/src/main/webapp/translations/fr.json b/guacamole/src/main/webapp/translations/fr.json
index 9407178..cc3e584 100644
--- a/guacamole/src/main/webapp/translations/fr.json
+++ b/guacamole/src/main/webapp/translations/fr.json
@@ -91,6 +91,8 @@
         "ERROR_UPLOAD_31D"     : "Trop de fichiers sont actuellement transférés. Merci d'attendre que les transferts en cours soient terminés et de réessayer plus tard.", 
         "ERROR_UPLOAD_DEFAULT" : "Une erreur interne est apparue dans le serveur Guacamole et la connexion a été fermée. Si le problème persiste, merci de notifier l'administrateur ou de regarder les journaux système.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Texte copié/coupé dans Guacamole apparaîtra ici. Changer le texte ci dessous affectera le presse-papiers distant.",
         "HELP_INPUT_METHOD_NONE"   : "Aucune méthode de saisie utilisée. Clavier accepté depuis un clavier physique connecté.",
         "HELP_INPUT_METHOD_OSK"    : "Affiche et utilise la saisie du clavier virtuel intégré dans Guacamole. Le clavier virtuel permet d'utiliser des combinaisons de touches autrement impossibles (comme Ctrl-Alt-Supp).",
@@ -356,6 +358,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Authentification",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Paramètres de base",
+        "SECTION_HEADER_CLIPBOARD"          : "Presse-papiers",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Redirection Périphérique",
         "SECTION_HEADER_DISPLAY"            : "Affichage",
         "SECTION_HEADER_NETWORK"            : "Réseau",
@@ -406,6 +409,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentification",
+        "SECTION_HEADER_CLIPBOARD"      : "Presse-papiers",
         "SECTION_HEADER_DISPLAY"        : "Affichage",
         "SECTION_HEADER_NETWORK"        : "Réseau",
         "SECTION_HEADER_SESSION"        : "Session / Environnement",
@@ -450,6 +454,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentification",
+        "SECTION_HEADER_CLIPBOARD"      : "Presse-papiers",
         "SECTION_HEADER_DISPLAY"        : "Affichage",
         "SECTION_HEADER_NETWORK"        : "Réseau"
 
@@ -466,6 +471,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Activer son:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Activer SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Nom d'hôte:",
+        "FIELD_HEADER_USERNAME"         : "Identifiant:",
         "FIELD_HEADER_PASSWORD"         : "Mot de passe:",
         "FIELD_HEADER_PORT"             : "Port:",
         "FIELD_HEADER_READ_ONLY"        : "Lecture seule:",
diff --git a/guacamole/src/main/webapp/translations/it.json b/guacamole/src/main/webapp/translations/it.json
index 442e709..7437b31 100644
--- a/guacamole/src/main/webapp/translations/it.json
+++ b/guacamole/src/main/webapp/translations/it.json
@@ -87,6 +87,8 @@
         "ERROR_UPLOAD_31D"     : "Ci sono troppi file in coda per il trasferimento. Attendi che siano completati i trasferimenti in atto e riprova.",
         "ERROR_UPLOAD_DEFAULT" : "Si è verificato un errore sul server e la connessione è stata chiusa. Riprova o contatta il tuo amministratore di sistema.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Il testo copiato/tagliato appare qui. I cambiamenti effettuati al testo qui sotto saranno riportati negli appunti remoti.",
         "HELP_INPUT_METHOD_NONE"   : "Non c'è nessun metodo di immissione. L'input da tastiera è accettato da una tastiera fisica connessa.",
         "HELP_INPUT_METHOD_OSK"    : "Mostra e accetta input dalla tastiera su schermo. La tastiera su schermo ti permette di scrivere combinazioni di tasti altrimenti mipossibli (ad esempio Ctrl-Alt-Canc).",
@@ -332,6 +334,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Authentication",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Basic Settings",
+        "SECTION_HEADER_CLIPBOARD"          : "Appunti",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Device Redirection",
         "SECTION_HEADER_DISPLAY"            : "Display",
         "SECTION_HEADER_NETWORK"            : "Network",
@@ -372,6 +375,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentication",
+        "SECTION_HEADER_CLIPBOARD"      : "Appunti",
         "SECTION_HEADER_DISPLAY"        : "Display",
         "SECTION_HEADER_NETWORK"        : "Network",
         "SECTION_HEADER_SFTP"           : "SFTP"
@@ -407,6 +411,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authentication",
+        "SECTION_HEADER_CLIPBOARD"      : "Appunti",
         "SECTION_HEADER_DISPLAY"        : "Display",
         "SECTION_HEADER_NETWORK"        : "Network"
 
@@ -422,6 +427,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Enable audio:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Enable SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Hostname:",
+        "FIELD_HEADER_USERNAME"         : "Username:",
         "FIELD_HEADER_PASSWORD"         : "Password:",
         "FIELD_HEADER_PORT"             : "Port:",
         "FIELD_HEADER_READ_ONLY"        : "Read-only:",
diff --git a/guacamole/src/main/webapp/translations/ja.json b/guacamole/src/main/webapp/translations/ja.json
new file mode 100644
index 0000000..c955902
--- /dev/null
+++ b/guacamole/src/main/webapp/translations/ja.json
@@ -0,0 +1,747 @@
+{
+    
+    "NAME" : "日本語",
+    
+    "APP" : {
+
+        "ACTION_CANCEL"             : "キャンセル",
+        "ACTION_CLONE"              : "コピー",
+        "ACTION_CONTINUE"           : "次へ",
+        "ACTION_DELETE"             : "削除",
+        "ACTION_DELETE_SESSIONS"    : "セッションの切断",
+        "ACTION_DOWNLOAD"           : "ダウンロード",
+        "ACTION_LOGIN"              : "ログイン",
+        "ACTION_LOGOUT"             : "ログアウト",
+        "ACTION_MANAGE_CONNECTIONS" : "接続",
+        "ACTION_MANAGE_PREFERENCES" : "ユーザ設定",
+        "ACTION_MANAGE_SETTINGS"    : "設定",
+        "ACTION_MANAGE_SESSIONS"    : "アクティブなセッション",
+        "ACTION_MANAGE_USERS"       : "ユーザ",
+        "ACTION_MANAGE_USER_GROUPS" : "グループ",
+        "ACTION_NAVIGATE_BACK"      : "戻る",
+        "ACTION_NAVIGATE_HOME"      : "ホーム",
+        "ACTION_SAVE"               : "保存",
+        "ACTION_SEARCH"             : "検索",
+        "ACTION_SHARE"              : "シェア",
+        "ACTION_UPDATE_PASSWORD"    : "パスワードの更新",
+        "ACTION_VIEW_HISTORY"       : "履歴",
+
+        "DIALOG_HEADER_ERROR" : "エラー",
+
+        "ERROR_PAGE_UNAVAILABLE"  : "エラーが発生したため、この操作を完了できませんでした。問題が解決しない場合は、システム管理者に連絡するか、システムログを確認してください。",
+
+        "ERROR_PASSWORD_BLANK"    : "パスワードが入力されていません。",
+        "ERROR_PASSWORD_MISMATCH" : "パスワードが一致しません。",
+        
+        "FIELD_HEADER_PASSWORD"       : "パスワード:",
+        "FIELD_HEADER_PASSWORD_AGAIN" : "パスワード確認:",
+
+        "FIELD_PLACEHOLDER_FILTER" : "フィルタ",
+
+        "INFO_ACTIVE_USER_COUNT" : "現在以下のユーザが利用中です。 {USERS} {USERS, plural, one{user} other{users}}.",
+
+        "TEXT_ANONYMOUS_USER"   : "匿名ユーザ"
+
+    },
+
+    "CLIENT" : {
+
+        "ACTION_ACKNOWLEDGE"               : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CLEAR_COMPLETED_TRANSFERS" : "クリア",
+        "ACTION_DISCONNECT"                : "切断",
+        "ACTION_LOGOUT"                    : "@:APP.ACTION_LOGOUT",
+        "ACTION_NAVIGATE_BACK"             : "@:APP.ACTION_NAVIGATE_BACK",
+        "ACTION_NAVIGATE_HOME"             : "@:APP.ACTION_NAVIGATE_HOME",
+        "ACTION_RECONNECT"                 : "再接続",
+        "ACTION_SAVE_FILE"                 : "@:APP.ACTION_SAVE",
+        "ACTION_SHARE"                     : "@:APP.ACTION_SHARE",
+        "ACTION_UPLOAD_FILES"              : "ファイルアップロード",
+
+        "DIALOG_HEADER_CONNECTING"       : "接続",
+        "DIALOG_HEADER_CONNECTION_ERROR" : "接続エラー",
+        "DIALOG_HEADER_DISCONNECTED"     : "切断",
+
+        "ERROR_CLIENT_201": "サーバーがビジー状態のため、この接続は切断されました。数分待ってからもう一度お試しください。",
+        "ERROR_CLIENT_202": "リモートデスクトップの応答に時間がかかりすぎるため、Guacamoleサーバが接続を切断しました。再試行するか、システム管理者に連絡してください。",
+        "ERROR_CLIENT_203": "リモートデスクトップサーバーでエラーが発生し、接続を切断しました。再試行するかシステム管理者に連絡してください。",
+        "ERROR_CLIENT_207": "現在リモートデスクトップサーバーにアクセスできません。問題が解決しない場合、システム管理者に連絡するか、システムログを確認してください。",
+        "ERROR_CLIENT_208": "リモートデスクトップサーバーは現在利用できません。問題が解決しない場合、システム管理者に連絡するか、システムログを確認してください。",
+        "ERROR_CLIENT_209": "リモートデスクトップサーバーが他の接続と競合しています。後でもう一度やり直してください。",
+        "ERROR_CLIENT_20A": "リモートデスクトップサーバーが停止しているため接続を閉じました。システム管理者に連絡するか、システム設定を確認してください。",
+        "ERROR_CLIENT_20B": "リモートデスクトップサーバーが強制的に接続を切断しました。システム管理者に連絡するか、システムログを確認してください。",
+        "ERROR_CLIENT_301": "ログインに失敗しました。再接続してからもう一度お試しください。",
+        "ERROR_CLIENT_303": "リモートデスクトップサーバーがこの接続へのアクセスを拒否しました。アクセスが必要な場合は、システム管理者にアカウントのアクセスを許可を依頼するか、システム設定を確認してください。",
+        "ERROR_CLIENT_308": "ブラウザからの応答が十分でないため、Guacamoleサーバーが接続を切断しました。これは一般的にネットワークの問題が原因です。ネットワークの状態を確認して、もう一度やり直してください。 ",
+        "ERROR_CLIENT_31D": "同時接続の使用制限に達したため、Guacamoleサーバーはこの接続へのアクセスを拒否しています。1つ以上の接続を閉じてからやり直してください。",
+        "ERROR_CLIENT_DEFAULT": "Guacamoleサーバ内で内部エラーが発生し、接続が終了しました。問題が解決しない場合、システム管理者に連絡するか、システムログを確認してください。",
+
+        "ERROR_TUNNEL_201": "アクティブな接続が多すぎるため、Guacamoleサーバーはこの接続を拒否しました。数分待ってからもう一度お試しください。",
+        "ERROR_TUNNEL_202": "サーバーの応答に時間がかかりすぎるため、接続が切断されました。通常はネットワーク問題によって引き起こされます。",
+        "ERROR_TUNNEL_203": "サーバーでエラーが発生し、接続を切断しました。再試行するかシステム管理者に連絡してください。",
+        "ERROR_TUNNEL_204": "指定された接続は存在しません。接続名を確認してもう一度やり直してください。",
+        "ERROR_TUNNEL_205": "この接続は現在使用中です。同時アクセスは許可されていません。後ほどやり直してください。",
+        "ERROR_TUNNEL_207": "現在Guacamoleサーバーにアクセスできません。ネットワークの状態を確認してもう一度やり直してください。",
+        "ERROR_TUNNEL_208": "Guacamoleサーバーは接続を受け付けていません。ネットワークの状態を確認してもう一度やり直してください。",
+        "ERROR_TUNNEL_301": "あなたはログインしていないため、この接続にアクセスする権限がありません。ログインしてからやり直してください。",
+        "ERROR_TUNNEL_303": "この接続にアクセスする権限がありません。アクセスが必要な場合は、システム管理者に許可を依頼するか、システム設定を確認してください。",
+        "ERROR_TUNNEL_308": "ブラウザからの応答がないため、Guacamoleサーバーが接続を切断しました。一般的にネットワークの問題が原因です。ネットワークの状態を確認して、もう一度やり直してください。 ",
+        "ERROR_TUNNEL_31D": "同時接続の使用制限に達したため、Guacamoleサーバーはこの接続へのアクセスを拒否しています。1つ以上の接続を閉じてからやり直してください。",
+        "ERROR_TUNNEL_DEFAULT": "Guacamoleサーバー内で内部エラーが発生し、接続が終了しました。問題が解決しない場合、システム管理者に連絡するか、システムログを確認してください。",
+
+        "ERROR_UPLOAD_100": "ファイル転送がサポートされていないか有効になっていません。システム管理者に連絡するか、システムログを確認してください。",
+        "ERROR_UPLOAD_201": "現在転送中のファイルが多すぎます。転送が完了するのを待ってからやり直してください。",
+        "ERROR_UPLOAD_202": "リモートデスクトップサーバーの応答に時間がかかりすぎるため、ファイルを転送できません。やりなおすかシステム管理者に連絡してください。",
+        "ERROR_UPLOAD_203": "転送中にリモートデスクトップサーバーでエラーが発生しました。もう一度やり直すか、システム管理者に連絡してください。",
+        "ERROR_UPLOAD_204": "ファイル転送先が存在しません。宛先が存在することを確認してやり直してください。",
+        "ERROR_UPLOAD_205": "ファイル転送先は現在ロックされています。進行中のタスクが完了するのを待ってからやり直してください。 ",
+        "ERROR_UPLOAD_301": "あなたはログインしていないため、このファイルをアップロードする権限がありません。ログインしてからやり直してください。",
+        "ERROR_UPLOAD_303": "このファイルをアップロードする権限がありません。アクセスが必要な場合は、システム設定を確認するか、システム管理者に確認してください。",
+        "ERROR_UPLOAD_308": "ファイル転送が停止しています。これは一般的にネットワーク速度が非常に遅いなどのネットワークの問題が原因で発生します。",
+        "ERROR_UPLOAD_31D": "現在転送中のファイルが多すぎます。転送が完了するのを待ってからやり直してください。",
+        "ERROR_UPLOAD_DEFAULT": "Guacamoleサーバ内で内部エラーが発生し、接続が終了しました。それでも問題が解決しない場合、システム管理者に連絡するか、システムログを確認してください。",
+
+        "HELP_CLIPBOARD": "Guacamole内でコピー/カットされたテキストがここに表示されます。以下のテキストを変更するとリモートクリップボードに影響します。 ",
+        "HELP_INPUT_METHOD_NONE": "入力方法は指定されていません。キーボード入力は接続された物理的なキーボードから受け付けられます。 ",
+        "HELP_INPUT_METHOD_OSK": "内蔵のGuacamoleオンスクリーンキーボードからの入力を表示し、受け入れます。オンスクリーンキーボードを使用すると、不可能な場合もあるキーの組み合わせを入力できます(Ctrl-Alt-Delなど)。",
+        "HELP_INPUT_METHOD_TEXT": "テキストの入力を許可し、入力されたテキストに基づいてキーボードイベントをエミュレートします。これはスマートフォンのような物理的なキーボードがない機器に必要です。 ",
+        "HELP_MOUSE_MODE": "タッチに対するリモートマウスの動作を決定します。 ",
+        "HELP_MOUSE_MODE_ABSOLUTE": "タッチによってクリックを行います。タッチの位置でクリックしたとみなされます。 ",
+        "HELP_MOUSE_MODE_RELATIVE": "マウスポインタをドラッグしてからクリックします。マウスポインタの位置でクリックしたとみなされます。 ",
+        "HELP_SHARE_LINK": "現在の接続は共有されており、次の{LINKS、multiple、one {link} other {links}}を持つ人なら誰でもアクセスできます:",
+
+        "INFO_CONNECTION_SHARED": "この接続は現在共有されています。",
+        "INFO_NO_FILE_TRANSFERS": "ファイル転送はありません。",
+
+
+        "NAME_INPUT_METHOD_NONE"   : "なし",
+        "NAME_INPUT_METHOD_OSK"    : "オンスクリーンキーボード",
+        "NAME_INPUT_METHOD_TEXT"   : "テキストインプット",
+        "NAME_MOUSE_MODE_ABSOLUTE" : "タッチスクリーン",
+        "NAME_MOUSE_MODE_RELATIVE" : "タッチパッド",
+
+        "SECTION_HEADER_CLIPBOARD"      : "クリップボード",
+        "SECTION_HEADER_DEVICES"        : "デバイス",
+        "SECTION_HEADER_DISPLAY"        : "ディスプレイ",
+        "SECTION_HEADER_FILE_TRANSFERS" : "ファイル転送",
+        "SECTION_HEADER_INPUT_METHOD"   : "インプットメソッド",
+        "SECTION_HEADER_MOUSE_MODE"     : "マウスエミュレートモード",
+
+        "TEXT_ZOOM_AUTO_FIT"              : "自動的にブラウザのサイズに合わせる",
+        "TEXT_CLIENT_STATUS_IDLE"         : "アイドル状態.",
+        "TEXT_CLIENT_STATUS_CONNECTING"   : "Guacamoleサーバに接続しています...",
+        "TEXT_CLIENT_STATUS_DISCONNECTED" : "切断されました。",
+        "TEXT_CLIENT_STATUS_UNSTABLE"     : "Guacamoleサーバへのネットワーク接続が不安定です。",
+        "TEXT_CLIENT_STATUS_WAITING"      : "Guacamoleサーバに接続しました。応答を待っています",
+        "TEXT_RECONNECT_COUNTDOWN"        : "再接続しています... {REMAINING} {REMAINING, plural, one{second} other{seconds}}..."
+
+    },
+
+    "FORM" : {
+
+        "HELP_SHOW_PASSWORD" : "パスワードを見る",
+        "HELP_HIDE_PASSWORD" : "パスワードを隠す"
+
+    },
+
+    "HOME" : {
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
+
+        "INFO_NO_RECENT_CONNECTIONS" : "最近の接続情報はありません。",
+        
+        "PASSWORD_CHANGED" : "パスワードが変更されました。",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"    : "全ての接続情報",
+        "SECTION_HEADER_RECENT_CONNECTIONS" : "最近の接続情報"
+
+    },
+
+    "LIST" : {
+
+        "TEXT_ANONYMOUS_USER" : "匿名"
+
+    },
+
+    "LOGIN": {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CONTINUE"    : "@:APP.ACTION_CONTINUE",
+        "ACTION_LOGIN"       : "@:APP.ACTION_LOGIN",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_INVALID_LOGIN" : "不正なログインです。",
+
+        "FIELD_HEADER_USERNAME" : "ユーザ名",
+        "FIELD_HEADER_PASSWORD" : "パスワード"
+
+    },
+
+    "MANAGE_CONNECTION" : {
+
+        "ACTION_ACKNOWLEDGE"          : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"               : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"                : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"               : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"                 : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "接続の削除",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_LOCATION" : "ロケーション:",
+        "FIELD_HEADER_NAME"     : "名前:",
+        "FIELD_HEADER_PROTOCOL" : "プロトコル:",
+
+        "FORMAT_HISTORY_START" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "INFO_CONNECTION_DURATION_UNKNOWN" : "--",
+        "INFO_CONNECTION_ACTIVE_NOW"       : "アクティブにする",
+        "INFO_CONNECTION_NOT_USED"         : "この接続はまだ使用されていません。",
+
+        "SECTION_HEADER_EDIT_CONNECTION" : "接続の編集",
+        "SECTION_HEADER_HISTORY"         : "使用履歴",
+        "SECTION_HEADER_PARAMETERS"      : "パラメータ",
+
+        "TABLE_HEADER_HISTORY_USERNAME"   : "ユーザ名",
+        "TABLE_HEADER_HISTORY_START"      : "開始時間",
+        "TABLE_HEADER_HISTORY_DURATION"   : "期間",
+        "TABLE_HEADER_HISTORY_REMOTEHOST" : "接続元",
+
+        "TEXT_CONFIRM_DELETE"   : "削除した接続は元に戻せません。この接続を削除してもよろしいですか?",
+        "TEXT_HISTORY_DURATION" : "@:APP.TEXT_HISTORY_DURATION"
+
+    },
+
+    "MANAGE_CONNECTION_GROUP" : {
+
+        "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"        : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"         : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"        : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"          : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "接続グループの削除",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_LOCATION" : "ロケーション:",
+        "FIELD_HEADER_NAME"     : "名前:",
+        "FIELD_HEADER_TYPE"     : "タイプ:",
+
+        "NAME_TYPE_BALANCING"       : "バランシング",
+        "NAME_TYPE_ORGANIZATIONAL"  : "組織",
+
+        "SECTION_HEADER_EDIT_CONNECTION_GROUP" : "接続グループの編集",
+
+        "TEXT_CONFIRM_DELETE" : "接続グループを削除した後に復元することはできません。この接続グループを削除してもよろしいですか?"
+
+    },
+
+    "MANAGE_SHARING_PROFILE" : {
+
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"       : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"      : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"        : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "共有プロファイルの削除",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_NAME"               : "名前:",
+        "FIELD_HEADER_PRIMARY_CONNECTION" : "プライマリ接続:",
+
+        "SECTION_HEADER_EDIT_SHARING_PROFILE" : "共有プロファイルの編集",
+        "SECTION_HEADER_PARAMETERS"           : "パラメータ",
+
+        "TEXT_CONFIRM_DELETE" : "削除した共有プロファイルは復元できません。この共有プロファイルを削除してもよろしいですか?"
+
+    },
+
+    "MANAGE_USER" : {
+
+        "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"        : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"         : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"        : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"          : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "ユーザ削除",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
+
+        "FIELD_HEADER_ADMINISTER_SYSTEM"             : "システム管理者:",
+        "FIELD_HEADER_CHANGE_OWN_PASSWORD"           : "自身のパスワードの変更:",
+        "FIELD_HEADER_CREATE_NEW_USERS"              : "ユーザの作成:",
+        "FIELD_HEADER_CREATE_NEW_USER_GROUPS"        : "ユーザグループの作成:",
+        "FIELD_HEADER_CREATE_NEW_CONNECTIONS"        : "接続の作成:",
+        "FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS"  : "接続グループの作成:",
+        "FIELD_HEADER_CREATE_NEW_SHARING_PROFILES"   : "共有プロファイルの作成:",
+        "FIELD_HEADER_PASSWORD"                      : "@:APP.FIELD_HEADER_PASSWORD",
+        "FIELD_HEADER_PASSWORD_AGAIN"                : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
+        "FIELD_HEADER_USERNAME"                      : "ユーザ名:",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_NO_USER_GROUPS" : "このユーザーは現在どのグループにも属していません。このセクションを展開してグループを追加してください。",
+
+        "INFO_READ_ONLY"                : "このユーザは編集できません。",
+        "INFO_NO_USER_GROUPS_AVAILABLE" : "利用可能なグループがありません。",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"     : "すべての接続",
+        "SECTION_HEADER_CONNECTIONS"         : "接続",
+        "SECTION_HEADER_CURRENT_CONNECTIONS" : "現在の接続",
+        "SECTION_HEADER_EDIT_USER"           : "ユーザの編集",
+        "SECTION_HEADER_PERMISSIONS"         : "権限",
+        "SECTION_HEADER_USER_GROUPS"         : "グループ",
+
+        "TEXT_CONFIRM_DELETE" : "削除したユーザーは元に戻せません。このユーザーを削除してもよろしいですか?"
+
+    },
+
+    "MANAGE_USER_GROUP" : {
+
+        "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"        : "@:APP.ACTION_CANCEL",
+        "ACTION_CLONE"         : "@:APP.ACTION_CLONE",
+        "ACTION_DELETE"        : "@:APP.ACTION_DELETE",
+        "ACTION_SAVE"          : "@:APP.ACTION_SAVE",
+
+        "DIALOG_HEADER_CONFIRM_DELETE" : "グループの削除",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_HEADER_ADMINISTER_SYSTEM"             : "@:MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
+        "FIELD_HEADER_CHANGE_OWN_PASSWORD"           : "@:MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD",
+        "FIELD_HEADER_CREATE_NEW_USERS"              : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
+        "FIELD_HEADER_CREATE_NEW_USER_GROUPS"        : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USER_GROUPS",
+        "FIELD_HEADER_CREATE_NEW_CONNECTIONS"        : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
+        "FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS"  : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
+        "FIELD_HEADER_CREATE_NEW_SHARING_PROFILES"   : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES",
+        "FIELD_HEADER_USER_GROUP_NAME"               : "グループ名:",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_NO_USER_GROUPS"        : "このグループは現在どのグループにも属していません。グループを追加するにはこのセクションを展開してください。",
+        "HELP_NO_MEMBER_USER_GROUPS" : "このグループには現在グループが含まれていません。このセクションを展開してグループを追加してください。",
+        "HELP_NO_MEMBER_USERS"       : "このグループには現在ユーザーが含まれていません。ユーザーを追加するにはこのセクションを展開してください。",
+
+        "INFO_READ_ONLY"                : "このグループは編集できません",
+        "INFO_NO_USER_GROUPS_AVAILABLE" : "@:MANAGE_USER.INFO_NO_USER_GROUPS_AVAILABLE",
+        "INFO_NO_USERS_AVAILABLE"       : "利用可能なユーザがいません。",
+
+        "SECTION_HEADER_ALL_CONNECTIONS"     : "@:MANAGE_USER.SECTION_HEADER_ALL_CONNECTIONS",
+        "SECTION_HEADER_CONNECTIONS"         : "@:MANAGE_USER.SECTION_HEADER_CONNECTIONS",
+        "SECTION_HEADER_CURRENT_CONNECTIONS" : "@:MANAGE_USER.SECTION_HEADER_CURRENT_CONNECTIONS",
+        "SECTION_HEADER_EDIT_USER_GROUP"     : "グループ編集",
+        "SECTION_HEADER_MEMBER_USERS"        : "メンバーユーザ",
+        "SECTION_HEADER_MEMBER_USER_GROUPS"  : "メンバーグループ",
+        "SECTION_HEADER_PERMISSIONS"         : "@:MANAGE_USER.SECTION_HEADER_PERMISSIONS",
+        "SECTION_HEADER_USER_GROUPS"         : "親グループ",
+
+        "TEXT_CONFIRM_DELETE" : "削除したグループは復元できません。このグループを削除してもよろしいですか?"
+
+    },
+    
+    "PROTOCOL_RDP" : {
+
+        "FIELD_HEADER_CLIENT_NAME"     : "クライアント名:",
+        "FIELD_HEADER_COLOR_DEPTH"     : "カラー深度:",
+        "FIELD_HEADER_CONSOLE"         : "管理者コンソール:",
+        "FIELD_HEADER_CONSOLE_AUDIO"   : "コンソールでの音声サポート:",
+        "FIELD_HEADER_CREATE_DRIVE_PATH" : "ドライブの自動作成:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH" : "ログの保存ディレクトリを自動的に作成する:",
+        "FIELD_HEADER_DISABLE_AUDIO"   : "オーディオの無効化:",
+        "FIELD_HEADER_DISABLE_AUTH"    : "認証の無効化:",
+        "FIELD_HEADER_DISABLE_COPY"    : "リモートデスクトップからのコピーを無効化:",
+        "FIELD_HEADER_DISABLE_PASTE"   : "クライアントからの貼り付けを無効化:",
+        "FIELD_HEADER_DOMAIN"          : "ドメイン:",
+        "FIELD_HEADER_DPI"             : "解像度 (DPI):",
+        "FIELD_HEADER_DRIVE_NAME"      : "ドライブ名:",
+        "FIELD_HEADER_DRIVE_PATH"      : "ドライブパス:",
+        "FIELD_HEADER_ENABLE_AUDIO_INPUT"         : "入力オーディオ(マイク)の有効化:",
+        "FIELD_HEADER_ENABLE_DESKTOP_COMPOSITION" : "デスクトップコンポジション(Aero)の有効化:",
+        "FIELD_HEADER_ENABLE_DRIVE"               : "ドライブの有効化:",
+        "FIELD_HEADER_ENABLE_FONT_SMOOTHING"      : "クリアタイプフォントの有効化:",
+        "FIELD_HEADER_ENABLE_FULL_WINDOW_DRAG"    : "フルウィンドウドラッグの有効化:",
+        "FIELD_HEADER_ENABLE_MENU_ANIMATIONS"     : "メニューアニメーションの有効化:",
+        "FIELD_HEADER_DISABLE_BITMAP_CACHING"     : "ビットマップキャッシュの無効化:",
+        "FIELD_HEADER_DISABLE_OFFSCREEN_CACHING"  : "オフスクリーンキャッシュの無効化:",
+        "FIELD_HEADER_DISABLE_GLYPH_CACHING"      : "グリフキャッシュの無効化:",
+        "FIELD_HEADER_ENABLE_PRINTING"            : "印刷の有効化:",
+        "FIELD_HEADER_ENABLE_SFTP"     : "SFTPの有効化:",
+        "FIELD_HEADER_ENABLE_THEMING"             : "テーマの有効化:",
+        "FIELD_HEADER_ENABLE_WALLPAPER"           : "壁紙の有効化:",
+        "FIELD_HEADER_GATEWAY_DOMAIN"   : "ドメイン:",
+        "FIELD_HEADER_GATEWAY_HOSTNAME" : "ホスト名:",
+        "FIELD_HEADER_GATEWAY_PASSWORD" : "パスワード:",
+        "FIELD_HEADER_GATEWAY_PORT"     : "ポート:",
+        "FIELD_HEADER_GATEWAY_USERNAME" : "ユーザ名:",
+        "FIELD_HEADER_HEIGHT"          : "高さ:",
+        "FIELD_HEADER_HOSTNAME"        : "ホスト名:",
+        "FIELD_HEADER_IGNORE_CERT"     : "サーバ証明書を無視する:",
+        "FIELD_HEADER_INITIAL_PROGRAM" : "初期化プログラム:",
+        "FIELD_HEADER_LOAD_BALANCE_INFO" : "ロードバランス情報/クッキー:",
+        "FIELD_HEADER_PASSWORD"        : "パスワード:",
+        "FIELD_HEADER_PORT"            : "ポート:",
+        "FIELD_HEADER_PRINTER_NAME"    : "リダイレクトされたプリンタ名:",
+        "FIELD_HEADER_PRECONNECTION_BLOB" : "事前接続BLOB (VM ID):",
+        "FIELD_HEADER_PRECONNECTION_ID"   : "RDPソースID:",
+        "FIELD_HEADER_READ_ONLY"      : "読み取り専用:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "マウス動作の除外:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "画像/ストリームの除外:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "キーイベントの取得:",
+        "FIELD_HEADER_RECORDING_NAME" : "ログファイル名:",
+        "FIELD_HEADER_RECORDING_PATH" : "ログ保存ディレクトリ:",
+        "FIELD_HEADER_RESIZE_METHOD" : "サイズ変更方法:",
+        "FIELD_HEADER_REMOTE_APP_ARGS" : "パラメータ:",
+        "FIELD_HEADER_REMOTE_APP_DIR"  : "作業ディレクトリ:",
+        "FIELD_HEADER_REMOTE_APP"      : "プログラム:",
+        "FIELD_HEADER_SECURITY"        : "セキュリティモード:",
+        "FIELD_HEADER_SERVER_LAYOUT"   : "キーボードレイアウト:",
+        "FIELD_HEADER_SFTP_DIRECTORY"             : "デフォルトアップロードディレクトリ:",
+        "FIELD_HEADER_SFTP_HOST_KEY"              : "パブリックホストキー (Base64):",
+        "FIELD_HEADER_SFTP_HOSTNAME"              : "ホスト名:",
+        "FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL" : "SFTPキープアライブ間隔:",
+        "FIELD_HEADER_SFTP_PASSPHRASE"            : "パスフレーズ:",
+        "FIELD_HEADER_SFTP_PASSWORD"              : "パスワード:",
+        "FIELD_HEADER_SFTP_PORT"                  : "ポート:",
+        "FIELD_HEADER_SFTP_PRIVATE_KEY"           : "秘密鍵:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"        : "ファイルブラウザのルートディレクトリ:",
+        "FIELD_HEADER_SFTP_USERNAME"              : "ユーザ名:",
+        "FIELD_HEADER_STATIC_CHANNELS" : "静的チャンネル名:",
+        "FIELD_HEADER_USERNAME"        : "ユーザ名:",
+        "FIELD_HEADER_WIDTH"           : "幅:",
+
+        "FIELD_OPTION_COLOR_DEPTH_16"    : "Lowカラー (16ビット)",
+        "FIELD_OPTION_COLOR_DEPTH_24"    : "Trueカラー (24ビット)",
+        "FIELD_OPTION_COLOR_DEPTH_32"    : "Trueカラー (32ビット)",
+        "FIELD_OPTION_COLOR_DEPTH_8"     : "256カラー",
+
+        "FIELD_OPTION_RESIZE_METHOD_DISPLAY_UPDATE" : "\"ディスプレイアップデート\" 仮想チャンネル (RDP 8.1+)",
+        "FIELD_OPTION_RESIZE_METHOD_RECONNECT"      : "再接続",
+
+        "SECTION_HEADER_AUTHENTICATION"     : "認証",
+        "SECTION_HEADER_BASIC_PARAMETERS"   : "基本設定",
+        "SECTION_HEADER_DEVICE_REDIRECTION" : "デバイスリダイレクト",
+        "SECTION_HEADER_DISPLAY"            : "ディスプレイ",
+        "SECTION_HEADER_GATEWAY"            : "リモートデスクトップゲートウェイ",
+        "SECTION_HEADER_LOAD_BALANCING"     : "ロードバラシング",
+        "SECTION_HEADER_NETWORK"            : "ネットワーク",
+        "SECTION_HEADER_PERFORMANCE"        : "パフォーマンス",
+        "SECTION_HEADER_RECORDING"          : "スクリーンレコーディング",
+        "SECTION_HEADER_REMOTEAPP"          : "リモートアプリケーション"
+
+    },
+
+    "PROTOCOL_SSH" : {
+
+        "FIELD_HEADER_BACKSPACE"    : "Backspaceキーの送信:",
+        "FIELD_HEADER_COLOR_SCHEME" : "カラースキーマ:",
+        "FIELD_HEADER_COMMAND"      : "コマンドの実行:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH" : "ログの保存ディレクトリを自動的に作成する:",
+        "FIELD_HEADER_CREATE_TYPESCRIPT_PATH" : "タイプスクリプトの保存ディレクトリを自動的に作成する:",
+        "FIELD_HEADER_DISABLE_COPY"  : "端末からのコピーを無効化:",
+        "FIELD_HEADER_DISABLE_PASTE" : "クライアントからの貼り付けを無効化:",
+        "FIELD_HEADER_FONT_NAME"     : "フォント名:",
+        "FIELD_HEADER_FONT_SIZE"     : "フォントサイズ:",
+        "FIELD_HEADER_ENABLE_SFTP"   : "SFTPの有効化:",
+        "FIELD_HEADER_HOST_KEY"      : "公開鍵(Base64):",
+        "FIELD_HEADER_HOSTNAME"      : "ホスト名:",
+        "FIELD_HEADER_USERNAME"      : "ユーザ名:",
+        "FIELD_HEADER_PASSWORD"      : "パスワード:",
+        "FIELD_HEADER_PASSPHRASE"    : "パスフレーズ:",
+        "FIELD_HEADER_PORT"          : "ポート:",
+        "FIELD_HEADER_PRIVATE_KEY"   : "秘密鍵:",
+        "FIELD_HEADER_READ_ONLY"     : "読み取り専用:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "マウス動作の除外:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "画像/ストリームの除外:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "キーイベントの取得:",
+        "FIELD_HEADER_RECORDING_NAME" : "ログファイル名:",
+        "FIELD_HEADER_RECORDING_PATH" : "ログ保存ディレクトリ",
+        "FIELD_HEADER_SERVER_ALIVE_INTERVAL" : "キープアライブ間隔:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"   : "ファイルブラウザのルートディレクトリ:",
+        "FIELD_HEADER_TERMINAL_TYPE"   : "ターミナルタイプ:",
+        "FIELD_HEADER_TYPESCRIPT_NAME" : "タイプスクリプト名:",
+        "FIELD_HEADER_TYPESCRIPT_PATH" : "タイプスクリプトの保存ディレクトリ:",
+
+        "SECTION_HEADER_AUTHENTICATION" : "認証",
+        "SECTION_HEADER_BEHAVIOR"       : "ターミナルのふるまい",
+        "SECTION_HEADER_DISPLAY"        : "ディスプレイ",
+        "SECTION_HEADER_NETWORK"        : "ネットワーク",
+        "SECTION_HEADER_RECORDING"      : "スクリーンレコーディング",
+        "SECTION_HEADER_SESSION"        : "セッション / 環境",
+        "SECTION_HEADER_TYPESCRIPT"     : "タイプスクリプト (テキストの記録)"
+
+    },
+
+    "PROTOCOL_TELNET" : {
+
+        "FIELD_HEADER_BACKSPACE"      : "Backspaceキーの送信:",
+        "FIELD_HEADER_COLOR_SCHEME"   : "カラースキーマ:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH" : "ログの保存ディレクトリを自動的に作成する:",
+        "FIELD_HEADER_CREATE_TYPESCRIPT_PATH" : "タイプスクリプトの保存ディレクトリを自動的に作成する:",
+        "FIELD_HEADER_DISABLE_COPY"  : "端末からのコピーを無効化:",
+        "FIELD_HEADER_DISABLE_PASTE" : "クライアントからの貼り付けを無効化:",
+        "FIELD_HEADER_FONT_NAME"      : "フォント名:",
+        "FIELD_HEADER_FONT_SIZE"      : "フォントサイズ:",
+        "FIELD_HEADER_HOSTNAME"       : "ホスト名:",
+        "FIELD_HEADER_LOGIN_FAILURE_REGEX" : "ログイン失敗正規表現:",
+        "FIELD_HEADER_LOGIN_SUCCESS_REGEX" : "ログイン成功正規表現:",
+        "FIELD_HEADER_USERNAME"       : "ユーザ名:",
+        "FIELD_HEADER_USERNAME_REGEX" : "ユーザ名正規表現:",
+        "FIELD_HEADER_PASSWORD"       : "パスワード:",
+        "FIELD_HEADER_PASSWORD_REGEX" : "パスワード正規表現:",
+        "FIELD_HEADER_PORT"           : "ポート:",
+        "FIELD_HEADER_READ_ONLY"      : "読み取り専用:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "マウス動作の除外:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "画像/ストリームの除外:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "キーイベントの取得:",
+        "FIELD_HEADER_RECORDING_NAME" : "ログファイル名:",
+        "FIELD_HEADER_RECORDING_PATH" : "ログ保存ディレクトリ:",
+        "FIELD_HEADER_TERMINAL_TYPE"   : "ターミナルタイプ:",
+        "FIELD_HEADER_TYPESCRIPT_NAME" : "タイプスクリプト名:",
+        "FIELD_HEADER_TYPESCRIPT_PATH" : "タイプスクリプト保存ディレクトリ:",
+
+        "SECTION_HEADER_AUTHENTICATION" : "認証",
+        "SECTION_HEADER_BEHAVIOR"       : "ターミナルのふるまい",
+        "SECTION_HEADER_DISPLAY"        : "ディスプレイ",
+        "SECTION_HEADER_RECORDING"      : "スクリーンレコーディング",
+        "SECTION_HEADER_TYPESCRIPT"     : "タイプスクリプト (テキストの記録)",
+        "SECTION_HEADER_NETWORK"        : "ネットワーク"
+
+    },
+
+    "PROTOCOL_VNC" : {
+
+        "FIELD_HEADER_AUDIO_SERVERNAME" : "オーディオサーバ名:",
+        "FIELD_HEADER_CLIPBOARD_ENCODING" : "エンコード:",
+        "FIELD_HEADER_COLOR_DEPTH"      : "カラー深度:",
+        "FIELD_HEADER_CREATE_RECORDING_PATH" : "ログの保存ディレクトリを自動的に作成する::",
+        "FIELD_HEADER_CURSOR"           : "カーソル:",
+        "FIELD_HEADER_DEST_HOST"        : "宛先ホスト:",
+        "FIELD_HEADER_DEST_PORT"        : "宛先ポート:",
+        "FIELD_HEADER_ENABLE_AUDIO"     : "オーディオの有効化:",
+        "FIELD_HEADER_ENABLE_SFTP"      : "SFTPの有効化:",
+        "FIELD_HEADER_HOSTNAME"         : "ホスト名:",
+        "FIELD_HEADER_USERNAME"         : "ユーザ名:",
+        "FIELD_HEADER_PASSWORD"         : "パスワード:",
+        "FIELD_HEADER_PORT"             : "ポート:",
+        "FIELD_HEADER_READ_ONLY"        : "読み取り専用:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_MOUSE"  : "マウス動作の除外:",
+        "FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT" : "画像/ストリームの除外:",
+        "FIELD_HEADER_RECORDING_INCLUDE_KEYS"   : "キーイベントの取得:",
+        "FIELD_HEADER_RECORDING_NAME" : "ログファイル名:",
+        "FIELD_HEADER_RECORDING_PATH" : "ログ保存ディレクトリ:",
+        "FIELD_HEADER_SFTP_DIRECTORY"             : "デフォルトアップロードディレクトリ:",
+        "FIELD_HEADER_SFTP_HOST_KEY"              : "公開鍵 (Base64):",
+        "FIELD_HEADER_SFTP_HOSTNAME"              : "ホスト名:",
+        "FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL" : "SFTPキープアライブ間隔:",
+        "FIELD_HEADER_SFTP_PASSPHRASE"            : "パスフレーズ:",
+        "FIELD_HEADER_SFTP_PASSWORD"              : "パスワード:",
+        "FIELD_HEADER_SFTP_PORT"                  : "ポート:",
+        "FIELD_HEADER_SFTP_PRIVATE_KEY"           : "秘密鍵:",
+        "FIELD_HEADER_SFTP_ROOT_DIRECTORY"        : "ファイルブラウザのルートディレクトリ:",
+        "FIELD_HEADER_SFTP_USERNAME"              : "ユーザ名:",
+        "FIELD_HEADER_SWAP_RED_BLUE"    : "レッド・ブルー コンポーネントスワップ:",
+
+        "FIELD_OPTION_COLOR_DEPTH_8"     : "256カラー",
+        "FIELD_OPTION_COLOR_DEPTH_16"    : "Lowカラー (16ビット)",
+        "FIELD_OPTION_COLOR_DEPTH_24"    : "Trueカラー (24ビット)",
+        "FIELD_OPTION_COLOR_DEPTH_32"    : "Trueカラー (32ビット)",
+
+        "FIELD_OPTION_CURSOR_LOCAL"  : "ローカル",
+        "FIELD_OPTION_CURSOR_REMOTE" : "リモート",
+
+        "SECTION_HEADER_AUDIO"          : "オーティオ",
+        "SECTION_HEADER_AUTHENTICATION" : "認証",
+        "SECTION_HEADER_CLIPBOARD"      : "クリップボード",
+        "SECTION_HEADER_DISPLAY"        : "ディスプレイ",
+        "SECTION_HEADER_NETWORK"        : "ネットワーク",
+        "SECTION_HEADER_RECORDING"      : "スクリーンレコーディング",
+        "SECTION_HEADER_REPEATER"       : "VNCリピーター"
+
+    },
+
+    "SETTINGS" : {
+
+        "SECTION_HEADER_SETTINGS" : "設定"
+
+    },
+
+    "SETTINGS_CONNECTION_HISTORY" : {
+
+        "ACTION_DOWNLOAD" : "@:APP.ACTION_DOWNLOAD",
+        "ACTION_SEARCH"   : "@:APP.ACTION_SEARCH",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_CONNECTION_HISTORY" : "過去の接続履歴はここに表示されています。列の見出しをクリックしてソートすることができます。特定のレコードを検索するにはフィルタに検索キーワードを入力して、検索ボタンをクリックしてください。",
+
+        "INFO_CONNECTION_DURATION_UNKNOWN" : "--",
+        "INFO_NO_HISTORY"                  : "一致するレコードがありません。",
+
+        "TABLE_HEADER_SESSION_CONNECTION_NAME" : "接続名",
+        "TABLE_HEADER_SESSION_DURATION"        : "期間",
+        "TABLE_HEADER_SESSION_REMOTEHOST"      : "接続元",
+        "TABLE_HEADER_SESSION_STARTDATE"       : "開始時間",
+        "TABLE_HEADER_SESSION_USERNAME"        : "ユーザ名",
+
+        "TEXT_HISTORY_DURATION" : "@:APP.TEXT_HISTORY_DURATION"
+
+    },
+
+    "SETTINGS_CONNECTIONS" : {
+
+        "ACTION_ACKNOWLEDGE"          : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_CONNECTION"       : "接続の追加",
+        "ACTION_NEW_CONNECTION_GROUP" : "グループの追加",
+        "ACTION_NEW_SHARING_PROFILE"  : "共有プロファイルの追加",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "HELP_CONNECTIONS"   : "接続をクリックまたはタップすることで、管理画面が表示されます。権限に応じて接続のプロパティが変更できます。",
+        
+        "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
+
+        "SECTION_HEADER_CONNECTIONS"     : "接続"
+
+    },
+
+    "SETTINGS_PREFERENCES" : {
+
+        "ACTION_ACKNOWLEDGE"        : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"             : "@:APP.ACTION_CANCEL",
+        "ACTION_UPDATE_PASSWORD"    : "@:APP.ACTION_UPDATE_PASSWORD",
+
+        "DIALOG_HEADER_ERROR"    : "@:APP.DIALOG_HEADER_ERROR",
+
+        "ERROR_PASSWORD_BLANK"    : "@:APP.ERROR_PASSWORD_BLANK",
+        "ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
+
+        "FIELD_HEADER_LANGUAGE"           : "表示言語:",
+        "FIELD_HEADER_PASSWORD"           : "パスワード:",
+        "FIELD_HEADER_PASSWORD_OLD"       : "現在のパスワード:",
+        "FIELD_HEADER_PASSWORD_NEW"       : "新しいパスワード:",
+        "FIELD_HEADER_PASSWORD_NEW_AGAIN" : "新しいパスワード(確認):",
+        "FIELD_HEADER_USERNAME"           : "ユーザ名:",
+        
+        "HELP_DEFAULT_INPUT_METHOD" : "デフォルトの入力メソッドは、Guacamoleがどのようにキーボード入力を受け取るかを設定します。この設定の変更は、モバイルデバイスまたはIMEを通して入力を行う際に必要です。",
+        "HELP_DEFAULT_MOUSE_MODE"   : "デフォルトのマウスエミュレーションモードは、タッチに関して新しい接続でリモートマウスがどのように動作するかを決定します。この設定は、Guacamoleメニュー内で接続ごとに上書きすることができます。",
+        "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_LOCALE"             : "Guacamoleno言語を変更するには、下の言語を選択してください。選択可能な言語は、インストールされている言語によって異なります。",
+        "HELP_MOUSE_MODE_ABSOLUTE"  : "@:CLIENT.HELP_MOUSE_MODE_ABSOLUTE",
+        "HELP_MOUSE_MODE_RELATIVE"  : "@:CLIENT.HELP_MOUSE_MODE_RELATIVE",
+        "HELP_UPDATE_PASSWORD"      : "パスワードを変更する場合は、下に現在のパスワードと新しいパスワードを入力して、[パスワードの更新]をクリックしてください。変更はすぐに有効になります。",
+
+        "INFO_PASSWORD_CHANGED" : "パスワードが変更されました。",
+
+        "NAME_INPUT_METHOD_NONE" : "@:CLIENT.NAME_INPUT_METHOD_NONE",
+        "NAME_INPUT_METHOD_OSK"  : "@:CLIENT.NAME_INPUT_METHOD_OSK",
+        "NAME_INPUT_METHOD_TEXT" : "@:CLIENT.NAME_INPUT_METHOD_TEXT",
+
+        "SECTION_HEADER_DEFAULT_INPUT_METHOD" : "デフォルトの入力方法",
+        "SECTION_HEADER_DEFAULT_MOUSE_MODE"   : "デフォルトのマウスエミュレーションモード",
+        "SECTION_HEADER_UPDATE_PASSWORD"      : "パスワード変更"
+
+    },
+
+    "SETTINGS_USERS" : {
+
+        "ACTION_ACKNOWLEDGE"   : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_USER"      : "ユーザ追加",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_USERS" : "ユーザをクリックまたはタップすることで、ユーザを管理できます。権限に応じてユーザ情報の変更を行うことができます。",
+
+        "SECTION_HEADER_USERS"       : "ユーザ",
+
+        "TABLE_HEADER_FULL_NAME"   : "フルネーム",
+        "TABLE_HEADER_LAST_ACTIVE" : "最後にアクティブになった時期",
+        "TABLE_HEADER_ORGANIZATION" : "組織",
+        "TABLE_HEADER_USERNAME"    : "ユーザ名"
+
+    },
+
+    "SETTINGS_USER_GROUPS" : {
+
+        "ACTION_ACKNOWLEDGE"    : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_NEW_USER_GROUP" : "グループ追加",
+
+        "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
+
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
+        "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_USER_GROUPS" : "グループをクリックまたはタップすることで、グループを管理できます。権限に応じてグループ情報の変更を行うことができます。",
+
+        "SECTION_HEADER_USER_GROUPS" : "グループ",
+
+        "TABLE_HEADER_USER_GROUP_NAME" : "グループ名"
+
+    },
+
+    "SETTINGS_SESSIONS" : {
+        
+        "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
+        "ACTION_CANCEL"      : "@:APP.ACTION_CANCEL",
+        "ACTION_DELETE"      : "強制切断",
+        
+        "DIALOG_HEADER_CONFIRM_DELETE" : "セッションの強制切断",
+        "DIALOG_HEADER_ERROR"          : "@:APP.DIALOG_HEADER_ERROR",
+        
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+        
+        "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
+
+        "HELP_SESSIONS" : "Guacamoleのアクティブなセッションが全て表示されています。 もしセッションを強制切断したい場合、 チェックボックスにチェックを入れて、強制切断ボタンをクリックしてください。",
+        
+        "INFO_NO_SESSIONS" : "アクティブセッションはありません",
+
+        "SECTION_HEADER_SESSIONS" : "アクティブセッション",
+        
+        "TABLE_HEADER_SESSION_CONNECTION_NAME" : "接続名",
+        "TABLE_HEADER_SESSION_REMOTEHOST"      : "接続元",
+        "TABLE_HEADER_SESSION_USERNAME"        : "ユーザ名",
+        
+        "TEXT_CONFIRM_DELETE" : "選択したすべてのセッションを強制終了しますか?これらのセッションを使用しているユーザーは直ちに切断されます。"
+
+    },
+
+    "USER_ATTRIBUTES" : {
+
+        "FIELD_HEADER_GUAC_EMAIL_ADDRESS"       : "メールアドレス:",
+        "FIELD_HEADER_GUAC_FULL_NAME"           : "名前:",
+        "FIELD_HEADER_GUAC_ORGANIZATION"        : "組織:",
+        "FIELD_HEADER_GUAC_ORGANIZATIONAL_ROLE" : "役職/役割:"
+
+    },
+
+    "USER_MENU" : {
+
+        "ACTION_LOGOUT"             : "@:APP.ACTION_LOGOUT",
+        "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS",
+        "ACTION_MANAGE_PREFERENCES" : "@:APP.ACTION_MANAGE_PREFERENCES",
+        "ACTION_MANAGE_SESSIONS"    : "@:APP.ACTION_MANAGE_SESSIONS",
+        "ACTION_MANAGE_SETTINGS"    : "@:APP.ACTION_MANAGE_SETTINGS",
+        "ACTION_MANAGE_USERS"       : "@:APP.ACTION_MANAGE_USERS",
+        "ACTION_MANAGE_USER_GROUPS" : "@:APP.ACTION_MANAGE_USER_GROUPS",
+        "ACTION_NAVIGATE_HOME"      : "@:APP.ACTION_NAVIGATE_HOME",
+        "ACTION_VIEW_HISTORY"       : "@:APP.ACTION_VIEW_HISTORY"
+
+    }
+
+}
diff --git a/guacamole/src/main/webapp/translations/nl.json b/guacamole/src/main/webapp/translations/nl.json
index 00ed1f6..6d8bb1e 100644
--- a/guacamole/src/main/webapp/translations/nl.json
+++ b/guacamole/src/main/webapp/translations/nl.json
@@ -91,6 +91,8 @@
         "ERROR_UPLOAD_31D"     : "Er worden momenteel te veel bestanden overdragen. Gelieve te wachten tot de bestaande bestandsoverdracht is voltooid, en probeer het opnieuw.",
         "ERROR_UPLOAD_DEFAULT" : "Er is een interne fout opgetreden op de Guacamole server, en de verbinding is beëindigd. Als het probleem aanhoudt, neem dan contact op met uw systeembeheerder of kijk in uw systeem logs.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Tekst gekopieerd / geknipt binnen Guacamole zal hier verschijnen. Wijzigingen in onderstaande tekst zal externe klembord beïnvloeden.",
         "HELP_INPUT_METHOD_NONE"   : "Geen invoer methode gebruiken. Toetsenbord invoer wordt geaccepteerd van een aangesloten, fysiek toetsenbord.",
         "HELP_INPUT_METHOD_OSK"    : "Weergave en accepteren van invoer via het ingebouwde Guacamole on-screen toetsenbord. Dit toetsenbord op het scherm maakt toetscombinaties mogelijk die anders onmogelijk zijn (zoals Ctrl-Alt-Del).",
@@ -365,6 +367,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Authenticatie",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Basis Instellingen",
+        "SECTION_HEADER_CLIPBOARD"          : "Klembord",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Apparaat Verbindingen",
         "SECTION_HEADER_DISPLAY"            : "Scherm",
         "SECTION_HEADER_NETWORK"            : "Netwerk",
@@ -422,6 +425,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authenticatie",
+        "SECTION_HEADER_CLIPBOARD"      : "Klembord",
         "SECTION_HEADER_DISPLAY"        : "Scherm",
         "SECTION_HEADER_NETWORK"        : "Netwerk",
         "SECTION_HEADER_RECORDING"      : "Scherm Opname",
@@ -474,6 +478,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Authenticatie",
+        "SECTION_HEADER_CLIPBOARD"      : "Klembord",
         "SECTION_HEADER_DISPLAY"        : "Scherm",
         "SECTION_HEADER_RECORDING"      : "Scherm Opname",
         "SECTION_HEADER_TYPESCRIPT"     : "Typescript (Tekst Sessie Opname)",
@@ -493,6 +498,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Inschakelen geluid:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Inschakelen SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Servernaam:",
+        "FIELD_HEADER_USERNAME"         : "Gebruikersnaam:",
         "FIELD_HEADER_PASSWORD"         : "Wachtwoord:",
         "FIELD_HEADER_PORT"             : "Poort:",
         "FIELD_HEADER_READ_ONLY"        : "Alleen lezen:",
diff --git a/guacamole/src/main/webapp/translations/no.json b/guacamole/src/main/webapp/translations/no.json
index bdec130..1cfcd25 100644
--- a/guacamole/src/main/webapp/translations/no.json
+++ b/guacamole/src/main/webapp/translations/no.json
@@ -93,6 +93,8 @@
         "ERROR_UPLOAD_31D"     : "For mange filer blir overført. Vent til aktive overføringer fullfører og prøv igjen.",
         "ERROR_UPLOAD_DEFAULT" : "En intern feil har oppstått i Guacamole og forbindelsen er terminert. Kontakt systemadministrator dersom problemet fortsetter eller sjekk systemloggene dine.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Tekst som er kopiert eller klippet i Guacamole vises her. Endringer i teksten under vil påvirke den eksterne utklippstavlen.",
         "HELP_INPUT_METHOD_NONE"   : "Ingen innenhet er brukt. Tastetrykk fra et fysisk tilkoblet tastatur blir akseptert.",
         "HELP_INPUT_METHOD_OSK"    : "Vis og aksepter tastetrykk fra det innebygde skjermtastaturet i Guacamole. Skjermtastaturet tillater tasting av tastekombinasjoner som ellers kan være umulig (f.eks. Ctrl-Alt-Del).",
@@ -348,6 +350,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "Autentisering",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "Grunnleggende Innstillinger",
+        "SECTION_HEADER_CLIPBOARD"          : "Utklippstavle",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "Enhet omdirigering",
         "SECTION_HEADER_DISPLAY"            : "Skjerm",
         "SECTION_HEADER_NETWORK"            : "Nettverk",
@@ -404,6 +407,7 @@
         "NAME" : "SSH",
 
         "SECTION_HEADER_AUTHENTICATION" : "Autentisering",
+        "SECTION_HEADER_CLIPBOARD"      : "Utklippstavle",
         "SECTION_HEADER_DISPLAY"        : "Skjerm",
         "SECTION_HEADER_NETWORK"        : "Nettverk",
         "SECTION_HEADER_RECORDING"      : "Skjermopptak",
@@ -455,6 +459,7 @@
         "NAME" : "Telnet",
 
         "SECTION_HEADER_AUTHENTICATION" : "Autentisering",
+        "SECTION_HEADER_CLIPBOARD"      : "Utklippstavle",
         "SECTION_HEADER_DISPLAY"        : "Skjerm",
         "SECTION_HEADER_RECORDING"      : "Skjermopptak",
         "SECTION_HEADER_TYPESCRIPT"     : "Typescript (Opptak av tekstsesjon)",
@@ -474,6 +479,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Aktiver lyd:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Aktiver SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Servernavn:",
+        "FIELD_HEADER_USERNAME"         : "Brukernavn:",
         "FIELD_HEADER_PASSWORD"         : "Passord:",
         "FIELD_HEADER_PORT"             : "Port:",
         "FIELD_HEADER_READ_ONLY"        : "Skrivebeskyttet:",
diff --git a/guacamole/src/main/webapp/translations/ru.json b/guacamole/src/main/webapp/translations/ru.json
index d6b2d47..63df958 100644
--- a/guacamole/src/main/webapp/translations/ru.json
+++ b/guacamole/src/main/webapp/translations/ru.json
@@ -88,6 +88,8 @@
         "ERROR_UPLOAD_31D"     : "Слишком много файлов передается в настоящий момент. Подождите завершения текущих передач и повторите попытку снова.",
         "ERROR_UPLOAD_DEFAULT" : "Соединение было прервано из-за внутренней ошибки сервера. Пожалуйста, попробуйте повторить попытку позднее или обратитесь к администратору.",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "Текст, скопированный или вырезанный внутри сеанса, появится в этом поле. Изменение текста также отразиться на буфере обмена удаленного рабочего стола.",
         "HELP_INPUT_METHOD_NONE"   : "Не выбран метод ввода. Ввод разрешен для физической клавиатуры.",
         "HELP_INPUT_METHOD_OSK"    : "Отображать и принимать ввод со встроенной экранной клавиатуры. Экранная клавиатура позволяет вводить любые комбинации, недоступные в других режимах (например Alt-Ctrl-Del).",
@@ -327,9 +329,11 @@
         "FIELD_OPTION_SERVER_LAYOUT_FR_FR_AZERTY" : "French (Azerty)",
         "FIELD_OPTION_SERVER_LAYOUT_IT_IT_QWERTY" : "Italian (Qwerty)",
         "FIELD_OPTION_SERVER_LAYOUT_SV_SE_QWERTY" : "Swedish (Qwerty)",
-	      "FIELD_OPTION_SERVER_LAYOUT_RU_RU_QWERTY" : "Russian (Qwerty)",
+        "FIELD_OPTION_SERVER_LAYOUT_RU_RU_QWERTY" : "Russian (Qwerty)",
 
-        "NAME" : "RDP"
+        "NAME" : "RDP",
+
+        "SECTION_HEADER_CLIPBOARD" : "Буфер обмена"
 
     },
 
@@ -362,7 +366,9 @@
         "FIELD_OPTION_FONT_SIZE_96"    : "96",
         "FIELD_OPTION_FONT_SIZE_EMPTY" : "",
 
-        "NAME" : "SSH"
+        "NAME" : "SSH",
+
+        "SECTION_HEADER_CLIPBOARD" : "Буфер обмена"
 
     },
 
@@ -393,7 +399,9 @@
         "FIELD_OPTION_FONT_SIZE_96"    : "96",
         "FIELD_OPTION_FONT_SIZE_EMPTY" : "",
 
-        "NAME" : "Telnet"
+        "NAME" : "Telnet",
+
+        "SECTION_HEADER_CLIPBOARD" : "Буфер обмена"
 
     },
 
@@ -407,6 +415,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "Включить звук:",
         "FIELD_HEADER_ENABLE_SFTP"      : "Включить SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "Название сервера:",
+        "FIELD_HEADER_USERNAME"         : "Имя пользователя:",
         "FIELD_HEADER_PASSWORD"         : "Пароль:",
         "FIELD_HEADER_PORT"             : "Порт:",
         "FIELD_HEADER_READ_ONLY"        : "Только просмотр:",
diff --git a/guacamole/src/main/webapp/translations/zh.json b/guacamole/src/main/webapp/translations/zh.json
index b940418..d8ad3d3 100644
--- a/guacamole/src/main/webapp/translations/zh.json
+++ b/guacamole/src/main/webapp/translations/zh.json
@@ -101,6 +101,8 @@
         "ERROR_UPLOAD_31D"     : "正在同时传输太多文件。请等待当前的传输任务完成后,再重试。",
         "ERROR_UPLOAD_DEFAULT" : "本连接因为Guacamole服务器出现了内部错误而被终止。如果问题持续,请通知您的系统管理员,或检查您的系统日志。",
 
+        "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
+
         "HELP_CLIPBOARD"           : "复制/剪切的文本将出现在这里。对下面文本内容所作的修改将会影响远程电脑上的剪贴板。",
         "HELP_INPUT_METHOD_NONE"   : "没有选择任何输入法。将从连接的物理键盘接受键盘输入。",
         "HELP_INPUT_METHOD_OSK"    : "显示并从内建的Guacamole屏幕键盘接受输入。屏幕键盘可以输入平常无法输入的按键组合(如Ctrl-Alt-Del等)。",
@@ -407,6 +409,7 @@
 
         "SECTION_HEADER_AUTHENTICATION"     : "认证",
         "SECTION_HEADER_BASIC_PARAMETERS"   : "基础设置",
+        "SECTION_HEADER_CLIPBOARD"          : "剪贴板",
         "SECTION_HEADER_DEVICE_REDIRECTION" : "设备重定向",
         "SECTION_HEADER_DISPLAY"            : "显示",
         "SECTION_HEADER_GATEWAY"            : "远程桌面网关",
@@ -477,6 +480,7 @@
 
         "SECTION_HEADER_AUTHENTICATION" : "认证",
         "SECTION_HEADER_BEHAVIOR"       : "终端行为",
+        "SECTION_HEADER_CLIPBOARD"      : "剪贴板",
         "SECTION_HEADER_DISPLAY"        : "显示",
         "SECTION_HEADER_NETWORK"        : "网络",
         "SECTION_HEADER_RECORDING"      : "屏幕录像",
@@ -538,6 +542,7 @@
 
         "SECTION_HEADER_AUTHENTICATION" : "认证",
         "SECTION_HEADER_BEHAVIOR"       : "终端行为",
+        "SECTION_HEADER_CLIPBOARD"      : "剪贴板",
         "SECTION_HEADER_DISPLAY"        : "显示",
         "SECTION_HEADER_RECORDING"      : "屏幕录像",
         "SECTION_HEADER_TYPESCRIPT"     : "打字稿(文本会话录像)",
@@ -557,6 +562,7 @@
         "FIELD_HEADER_ENABLE_AUDIO"     : "启用音频:",
         "FIELD_HEADER_ENABLE_SFTP"      : "启用SFTP:",
         "FIELD_HEADER_HOSTNAME"         : "主机名:",
+        "FIELD_HEADER_USERNAME"         : "用户名:",
         "FIELD_HEADER_PASSWORD"         : "密码:",
         "FIELD_HEADER_PORT"             : "端口:",
         "FIELD_HEADER_READ_ONLY"        : "只读:",