From a6876de0b1eef42631d9c7e0437eaf9001c859fb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 16 Nov 2020 13:55:45 -0800 Subject: [PATCH] Convert "guacamole-tunnel" web application into an equivalent Apache Guacamole extension. --- .gitignore | 3 - .../.gitignore | 2 - guacamole-auth-uds/pom.xml | 94 + .../guacamole/UDSAuthenticatedUser.java | 107 + .../guacamole/UDSAuthenticationProvider.java | 155 ++ .../java/org/openuds/guacamole/UDSModule.java | 33 +- .../config/ConfigurationService.java | 80 +- .../guacamole/config/package-info.java | 0 .../connection/ConnectionService.java | 0 .../guacamole/connection/package-info.java | 0 .../org/openuds/guacamole/package-info.java | 0 .../src/main/resources/guac-manifest.json | 18 + .../src/main/resources}/images/udsicon.png | Bin .../src/main/resources/translations/en.json | 7 + .../webapp/images/action-icons/guac-back.png | Bin .../webapp/images/action-icons/guac-close.png | Bin .../images/action-icons/guac-config.png | Bin .../images/action-icons/guac-delete.png | Bin .../images/action-icons/guac-first-page.png | Bin .../images/action-icons/guac-group-add.png | Bin .../images/action-icons/guac-last-page.png | Bin .../images/action-icons/guac-logout.png | Bin .../images/action-icons/guac-monitor-add.png | Bin .../images/action-icons/guac-next-page.png | Bin .../images/action-icons/guac-prev-page.png | Bin .../images/action-icons/guac-user-add.png | Bin .../main/webapp/images/arrows/arrows-d.png | Bin .../main/webapp/images/arrows/arrows-l.png | Bin .../main/webapp/images/arrows/arrows-r.png | Bin .../main/webapp/images/arrows/arrows-u.png | Bin .../webapp/images/group-icons/guac-closed.png | Bin .../webapp/images/group-icons/guac-open.png | Bin .../src/main/webapp/images/guac-mono-192.png | Bin .../main/webapp/images/guacamole-logo-144.png | Bin .../main/webapp/images/guacamole-logo-24.png | Bin .../main/webapp/images/guacamole-logo-64.png | Bin .../src/main/webapp/images/mouse/blank.cur | Bin .../src/main/webapp/images/mouse/blank.gif | Bin .../src/main/webapp/images/mouse/dot.gif | Bin .../webapp/images/noguacamole-logo-24.png | Bin .../src/main/webapp/images/progress.png | Bin .../images/protocol-icons/guac-monitor.png | Bin .../images/protocol-icons/guac-plug.png | Bin .../images/protocol-icons/guac-text.png | Bin .../webapp/images/settings/tablet-keys.png | Bin .../main/webapp/images/settings/touchpad.png | Bin .../webapp/images/settings/touchscreen.png | Bin .../main/webapp/images/settings/zoom-in.png | Bin .../main/webapp/images/settings/zoom-out.png | Bin .../webapp/images/user-icons/guac-user.png | Bin guacamole-tunnel/NOTICE | 5 - guacamole-tunnel/pom.xml | 170 -- .../apache/guacamole/tunnel/TunnelLoader.java | 41 - .../apache/guacamole/tunnel/TunnelModule.java | 116 - .../guacamole/tunnel/TunnelRequest.java | 371 --- .../tunnel/TunnelRequestService.java | 159 -- .../tunnel/http/HTTPTunnelRequest.java | 88 - .../RestrictedGuacamoleHTTPTunnelServlet.java | 70 - .../guacamole/tunnel/http/package-info.java | 23 - .../apache/guacamole/tunnel/package-info.java | 23 - ...ictedGuacamoleWebSocketTunnelEndpoint.java | 122 - .../websocket/WebSocketTunnelModule.java | 106 - .../websocket/WebSocketTunnelRequest.java | 67 - .../GuacamoleWebSocketTunnelServlet.java | 236 -- ...rictedGuacamoleWebSocketTunnelServlet.java | 54 - .../jetty8/WebSocketTunnelModule.java | 70 - .../tunnel/websocket/jetty8/package-info.java | 23 - .../GuacamoleWebSocketTunnelListener.java | 247 -- .../RestrictedGuacamoleWebSocketCreator.java | 74 - ...ictedGuacamoleWebSocketTunnelListener.java | 61 - ...rictedGuacamoleWebSocketTunnelServlet.java | 56 - .../jetty9/WebSocketTunnelModule.java | 70 - .../jetty9/WebSocketTunnelRequest.java | 73 - .../tunnel/websocket/jetty9/package-info.java | 24 - .../tunnel/websocket/package-info.java | 24 - .../GuacamoleWebSocketTunnelServlet.java | 269 -- ...rictedGuacamoleWebSocketTunnelServlet.java | 54 - .../tomcat/WebSocketTunnelModule.java | 70 - .../tunnel/websocket/tomcat/package-info.java | 25 - .../guacamole/UDSServletContextListener.java | 54 - .../src/main/webapp/WEB-INF/tunnel.properties | 5 - .../src/main/webapp/WEB-INF/web.xml | 55 - guacamole-tunnel/src/main/webapp/index.xhtml | 182 -- .../webapp/layouts/en-us-qwerty-mobile.xml | 312 --- .../src/main/webapp/layouts/en-us-qwerty.xml | 496 ---- guacamole-tunnel/src/main/webapp/readme.txt | 8 - .../src/main/webapp/scripts/client-ui.js | 2370 ----------------- .../src/main/webapp/scripts/guac-ui.js | 337 --- .../main/webapp/scripts/lib/blob/LICENSE.md | 30 - .../src/main/webapp/scripts/lib/blob/blob.js | 178 -- .../webapp/scripts/lib/filesaver/LICENSE.md | 30 - .../webapp/scripts/lib/filesaver/filesaver.js | 216 -- .../src/main/webapp/scripts/session.js | 160 -- .../src/main/webapp/styles/animation.css | 53 - .../src/main/webapp/styles/client.css | 779 ------ .../src/main/webapp/styles/keyboard.css | 153 -- .../src/main/webapp/styles/ui.css | 626 ----- 97 files changed, 425 insertions(+), 8909 deletions(-) rename {guacamole-tunnel => guacamole-auth-uds}/.gitignore (68%) create mode 100644 guacamole-auth-uds/pom.xml create mode 100644 guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticatedUser.java create mode 100644 guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticationProvider.java rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/UDSModule.java (68%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/config/ConfigurationService.java (73%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/config/package-info.java (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/connection/ConnectionService.java (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/connection/package-info.java (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/java/org/openuds/guacamole/package-info.java (100%) create mode 100644 guacamole-auth-uds/src/main/resources/guac-manifest.json rename {guacamole-tunnel/src/main/webapp => guacamole-auth-uds/src/main/resources}/images/udsicon.png (100%) create mode 100644 guacamole-auth-uds/src/main/resources/translations/en.json rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-back.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-close.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-config.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-delete.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-first-page.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-group-add.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-last-page.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-logout.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-monitor-add.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-next-page.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-prev-page.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/action-icons/guac-user-add.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/arrows/arrows-d.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/arrows/arrows-l.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/arrows/arrows-r.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/arrows/arrows-u.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/group-icons/guac-closed.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/group-icons/guac-open.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/guac-mono-192.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/guacamole-logo-144.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/guacamole-logo-24.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/guacamole-logo-64.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/mouse/blank.cur (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/mouse/blank.gif (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/mouse/dot.gif (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/noguacamole-logo-24.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/progress.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/protocol-icons/guac-monitor.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/protocol-icons/guac-plug.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/protocol-icons/guac-text.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/settings/tablet-keys.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/settings/touchpad.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/settings/touchscreen.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/settings/zoom-in.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/settings/zoom-out.png (100%) rename {guacamole-tunnel => guacamole-auth-uds}/src/main/webapp/images/user-icons/guac-user.png (100%) delete mode 100644 guacamole-tunnel/NOTICE delete mode 100644 guacamole-tunnel/pom.xml delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelLoader.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelModule.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/WebSocketTunnelModule.java delete mode 100644 guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/package-info.java delete mode 100644 guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSServletContextListener.java delete mode 100644 guacamole-tunnel/src/main/webapp/WEB-INF/tunnel.properties delete mode 100644 guacamole-tunnel/src/main/webapp/WEB-INF/web.xml delete mode 100644 guacamole-tunnel/src/main/webapp/index.xhtml delete mode 100644 guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty-mobile.xml delete mode 100644 guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty.xml delete mode 100644 guacamole-tunnel/src/main/webapp/readme.txt delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/client-ui.js delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/guac-ui.js delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/lib/blob/LICENSE.md delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/lib/blob/blob.js delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/LICENSE.md delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/filesaver.js delete mode 100644 guacamole-tunnel/src/main/webapp/scripts/session.js delete mode 100644 guacamole-tunnel/src/main/webapp/styles/animation.css delete mode 100644 guacamole-tunnel/src/main/webapp/styles/client.css delete mode 100644 guacamole-tunnel/src/main/webapp/styles/keyboard.css delete mode 100644 guacamole-tunnel/src/main/webapp/styles/ui.css diff --git a/.gitignore b/.gitignore index b45ed795..df239c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,6 @@ /client/administration/installer/UDSAdminInstaller/MSChart.exe /client/administration/installer/UDSAdminInstaller/UDSAdminSetup.exe -# /guacamole-tunnel/ -/guacamole-tunnel/target - # /linuxActor/ /linuxActor/udsactor_* diff --git a/guacamole-tunnel/.gitignore b/guacamole-auth-uds/.gitignore similarity index 68% rename from guacamole-tunnel/.gitignore rename to guacamole-auth-uds/.gitignore index 8d61509d..acb95da7 100644 --- a/guacamole-tunnel/.gitignore +++ b/guacamole-auth-uds/.gitignore @@ -2,8 +2,6 @@ *~ # Generated files -src/main/webapp/META-INF/ -src/main/webapp/generated/ target/ # IDE-specific configuration diff --git a/guacamole-auth-uds/pom.xml b/guacamole-auth-uds/pom.xml new file mode 100644 index 00000000..ff0099a1 --- /dev/null +++ b/guacamole-auth-uds/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + org.openuds.server + guacamole-auth-uds + jar + 2.5.0 + UDS Integration Extension for Apache Guacamole + https://github.com/dkmstr/openuds + + + UTF-8 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + + -Xlint:all + -Werror + + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack-dependencies + prepare-package + + unpack-dependencies + + + runtime + ${project.build.directory}/classes + + + + + + + + + + + + javax.servlet + servlet-api + provided + 2.5 + + + + + javax.ws.rs + jsr311-api + 1.1.1 + provided + + + + + org.apache.guacamole + guacamole-ext + 1.1.0 + provided + + + + + com.google.inject + guice + 3.0 + + + + + diff --git a/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticatedUser.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticatedUser.java new file mode 100644 index 00000000..076731ed --- /dev/null +++ b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticatedUser.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 Virtual Cable S.L. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Virtual Cable S.L. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.openuds.guacamole; + +import org.apache.guacamole.net.auth.AbstractAuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +/** + * A Guacamole user that was authenticated by an external UDS service. + */ +public class UDSAuthenticatedUser extends AbstractAuthenticatedUser { + + /** + * The AuthenticationProvider that authenticated this user. + */ + private final AuthenticationProvider authProvider; + + /** + * The credentials provided by this user when they authenticated. + */ + private final Credentials credentials; + + /** + * The GuacamoleConfiguration generated from the connection information + * returned by the external UDS service when the user authenticated. + */ + private final GuacamoleConfiguration config; + + /** + * Creates a new UDSAuthenticatedUser representing a Guacamole user that + * was authenticated by an external UDS service. + * + * @param authProvider + * The AuthenticationProvider that authenticated the user. + * + * @param credentials + * The credentials provided by the user when they authenticated. + * + * @param config + * The GuacamoleConfiguration generated from the connection information + * returned by the external UDS service when the user authenticated. + */ + public UDSAuthenticatedUser(AuthenticationProvider authProvider, + Credentials credentials, GuacamoleConfiguration config) { + this.authProvider = authProvider; + this.credentials = credentials; + this.config = config; + } + + @Override + public String getIdentifier() { + return AuthenticatedUser.ANONYMOUS_IDENTIFIER; + } + + @Override + public AuthenticationProvider getAuthenticationProvider() { + return authProvider; + } + + @Override + public Credentials getCredentials() { + return credentials; + } + + /** + * Returns the GuacamoleConfiguration generated from the connection + * information provided by the external UDS service when the user + * authenticated. + * + * @return + * The GuacamoleConfiguration generated from the connection information + * provided by the external UDS service when the user authenticated. + */ + public GuacamoleConfiguration getGuacamoleConfiguration() { + return config; + } + +} diff --git a/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticationProvider.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticationProvider.java new file mode 100644 index 00000000..6d10381f --- /dev/null +++ b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSAuthenticationProvider.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2020 Virtual Cable S.L. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Virtual Cable S.L. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.openuds.guacamole; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import java.util.Collections; +import javax.servlet.http.HttpServletRequest; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.form.Field; +import org.apache.guacamole.net.auth.AbstractAuthenticationProvider; +import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.credentials.CredentialsInfo; +import org.apache.guacamole.net.auth.credentials.GuacamoleInvalidCredentialsException; +import org.apache.guacamole.net.auth.simple.SimpleUserContext; +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.openuds.guacamole.connection.ConnectionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AuthenticationProvider implementation which authenticates users that are + * confirmed as authorized by an external UDS service. + */ +public class UDSAuthenticationProvider extends AbstractAuthenticationProvider { + + /** + * The name of the single connection that should be exposed to any user + * that authenticates via UDS. + */ + private static final String CONNECTION_NAME = "UDS"; + + /** + * The name of the query parameter that should contain the data sent to + * the UDS service for authentication. + */ + private static final String DATA_PARAMETER_NAME = "data"; + + /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(UDSAuthenticationProvider.class); + + /** + * Service for retrieving connection configuration information from the + * UDS service. + */ + private final ConnectionService connectionService; + + /** + * Creates a new UDSAuthenticationProvider which authenticates users + * against an external UDS service. + * + * @throws GuacamoleException + * If an error prevents guacamole.properties from being read. + */ + public UDSAuthenticationProvider() throws GuacamoleException { + + // Create an injector with OpenUDS- and Guacamole-specific services + // properly bound + Injector injector = Guice.createInjector( + new UDSModule() + ); + + // Pull instance of connection service from injector + connectionService = injector.getInstance(ConnectionService.class); + + } + + @Override + public String getIdentifier() { + return "uds"; + } + + @Override + public AuthenticatedUser authenticateUser(Credentials credentials) + throws GuacamoleException { + + HttpServletRequest request = credentials.getRequest(); + + // Pull OpenUDS-specific "data" parameter + String data = request.getParameter(DATA_PARAMETER_NAME); + if (data != null && !data.isEmpty()) { + + logger.debug("Retrieving connection configuration using data from \"{}\"...", data); + + // Retrieve connection information using provided data + GuacamoleConfiguration config = connectionService.getConnectionConfiguration(data); + if (config != null) { + + // Report successful authentication as a temporary, anonymous user, + // storing the retrieved connection configuration data for future use + return new UDSAuthenticatedUser(this, credentials, config); + + } + + } + + // Required parameter was missing or was invalid + throw new GuacamoleInvalidCredentialsException( + "Connection data was not provided or was rejected by UDS.", + new CredentialsInfo(Collections.singletonList( + new Field(DATA_PARAMETER_NAME, Field.Type.QUERY_PARAMETER) + )) + ); + + } + + @Override + public UserContext getUserContext(AuthenticatedUser authenticatedUser) + throws GuacamoleException { + + // Provide data only for users authenticated by this extension + if (!(authenticatedUser instanceof UDSAuthenticatedUser)) + return null; + + // Expose a single connection (derived from the "data" parameter + // provided during authentication) + return new SimpleUserContext(this, Collections.singletonMap( + CONNECTION_NAME, + ((UDSAuthenticatedUser) authenticatedUser).getGuacamoleConfiguration() + )); + + } + +} diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSModule.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSModule.java similarity index 68% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSModule.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSModule.java index 76e904d1..7163a0bc 100644 --- a/guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSModule.java +++ b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/UDSModule.java @@ -28,9 +28,10 @@ package org.openuds.guacamole; -import com.google.inject.servlet.ServletModule; -import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; -import org.apache.guacamole.tunnel.TunnelRequestService; +import com.google.inject.AbstractModule; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.environment.LocalEnvironment; import org.openuds.guacamole.config.ConfigurationService; import org.openuds.guacamole.connection.ConnectionService; @@ -38,18 +39,34 @@ import org.openuds.guacamole.connection.ConnectionService; * Guice module which binds classes required by the OpenUDS integration of * Apache Guacamole. */ -public class UDSModule extends ServletModule { +public class UDSModule extends AbstractModule { + + /** + * The Guacamole server environment. + */ + private final Environment environment; + + /** + * Creates a new UDSModule which binds classes required by the OpenUDS + * integration of Apache Guacamole, including an implementation of the + * Guacamole server {@link Environment}. + * + * @throws GuacamoleException + * If the guacamole.properties file cannot be read. + */ + public UDSModule() throws GuacamoleException { + this.environment = new LocalEnvironment(); + } @Override - protected void configureServlets() { + protected void configure() { - // Serve servlets, etc. with Guice - bind(GuiceContainer.class); + // Bind instance of Guacamole server environment + bind(Environment.class).toInstance(environment); // Bind UDS-specific services bind(ConfigurationService.class); bind(ConnectionService.class); - bind(TunnelRequestService.class); } diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/config/ConfigurationService.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/config/ConfigurationService.java similarity index 73% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/config/ConfigurationService.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/config/ConfigurationService.java index 0fdefc3d..9b300311 100644 --- a/guacamole-tunnel/src/main/java/org/openuds/guacamole/config/ConfigurationService.java +++ b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/config/ConfigurationService.java @@ -35,16 +35,16 @@ import java.io.FileReader; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.util.Properties; -import javax.servlet.ServletContext; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.properties.StringGuacamoleProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Service that provides access to configuration information stored within - * OpenUDS' tunnel.properties file. + * Service that provides access to OpenUDS-specific configuration information + * stored within guacamole.properties. */ @Singleton public class ConfigurationService { @@ -59,7 +59,14 @@ public class ConfigurationService { * whose content dictates the base URL of the service providing connection * configuration information. */ - private static final String UDSFILE_PROPERTY = "udsfile"; + private static final StringGuacamoleProperty UDSFILE_PROPERTY = new StringGuacamoleProperty() { + + @Override + public String getName() { + return "udsfile"; + } + + }; /** * The path beneath the OpenUDS service base URI (scheme + hostname) at @@ -69,9 +76,10 @@ public class ConfigurationService { private static final String UDS_CONNECTION_PATH = "/guacamole/"; /** - * The base URI (scheme + hostname) where OpenUDS is being served. + * The Guacamole server environment. */ - private final URI udsBaseURI; + @Inject + private Environment environment; /** * Parses the contents of the given file, reading the URI of the OpenUDS @@ -157,56 +165,6 @@ public class ConfigurationService { } - /** - * Creates a new ConfigurationService which provides access to the - * configuration information stored within the "/WEB-INF/tunnel.properties" - * file in the classpath. This file will be parsed immediately, but any - * resulting errors will simply be logged. If configuration information - * cannot be read, attempts to retrieve this information later through calls - * to the getters of this service will fail with appropriate exceptions. - * - * @param context - * The ServletContext associated with the servlet container which is - * serving this web application. - */ - @Inject - public ConfigurationService(ServletContext context) { - - // Read tunnel.properties - Properties config = new Properties(); - try { - config.load(context.getResourceAsStream("/WEB-INF/tunnel.properties")); - } - catch (IOException e) { - logger.error("Unable to read tunnel.properties.", e); - } - - // Parse URI from the UDS file (if defined) - URI parsedURI = null; - String udsFile = config.getProperty(UDSFILE_PROPERTY); - if (udsFile != null) { - - // Attempt to parse base URI from the UDS file, logging any failures - try { - parsedURI = getBaseURI(readServiceURI(udsFile)); - } - catch (GuacamoleException e) { - logger.error("OpenUDS service URI could not be parsed. This " - + "web application WILL NOT FUNCTION.", e); - } - - } - - // If no UDS file is defined, web application startup has failed - else - logger.error("Property \"{}\" not found within tunnel.properties. " - + "This web application WILL NOT FUNCTION.", UDSFILE_PROPERTY); - - // Assign the parsed URI, which may be null - udsBaseURI = parsedURI; - - } - /** * Returns the base URI of the OpenUDS service. All services providing data * to this Guacamole integration are hosted beneath this base URI. @@ -221,11 +179,11 @@ public class ConfigurationService { */ public URI getUDSBaseURI() throws GuacamoleException { - // Explicitly fail if the configuration was not successfully read - if (udsBaseURI == null) - throw new GuacamoleServerException("The UDS base URI is not defined."); + // Parse URI from the UDS file (if defined) + String udsFile = environment.getRequiredProperty(UDSFILE_PROPERTY); - return udsBaseURI; + // Attempt to parse base URI from the UDS file + return getBaseURI(readServiceURI(udsFile)); } diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/config/package-info.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/config/package-info.java similarity index 100% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/config/package-info.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/config/package-info.java diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/connection/ConnectionService.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/connection/ConnectionService.java similarity index 100% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/connection/ConnectionService.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/connection/ConnectionService.java diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/connection/package-info.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/connection/package-info.java similarity index 100% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/connection/package-info.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/connection/package-info.java diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/package-info.java b/guacamole-auth-uds/src/main/java/org/openuds/guacamole/package-info.java similarity index 100% rename from guacamole-tunnel/src/main/java/org/openuds/guacamole/package-info.java rename to guacamole-auth-uds/src/main/java/org/openuds/guacamole/package-info.java diff --git a/guacamole-auth-uds/src/main/resources/guac-manifest.json b/guacamole-auth-uds/src/main/resources/guac-manifest.json new file mode 100644 index 00000000..7a454953 --- /dev/null +++ b/guacamole-auth-uds/src/main/resources/guac-manifest.json @@ -0,0 +1,18 @@ +{ + + "guacamoleVersion" : "1.1.0", + + "name" : "UDS Integration Extension for Apache Guacamole", + "namespace" : "uds", + "largeIcon" : "images/udsicon.png", + "smallIcon" : "images/udsicon.png", + + "authProviders" : [ + "org.openuds.guacamole.UDSAuthenticationProvider" + ], + + "translations" : [ + "translations/en.json" + ] + +} diff --git a/guacamole-tunnel/src/main/webapp/images/udsicon.png b/guacamole-auth-uds/src/main/resources/images/udsicon.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/udsicon.png rename to guacamole-auth-uds/src/main/resources/images/udsicon.png diff --git a/guacamole-auth-uds/src/main/resources/translations/en.json b/guacamole-auth-uds/src/main/resources/translations/en.json new file mode 100644 index 00000000..2f77a9ca --- /dev/null +++ b/guacamole-auth-uds/src/main/resources/translations/en.json @@ -0,0 +1,7 @@ +{ + + "DATA_SOURCE_UDS" : { + "NAME" : "UDS" + } + +} diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-back.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-back.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-back.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-back.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-close.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-close.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-close.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-close.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-config.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-config.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-config.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-config.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-delete.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-delete.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-delete.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-delete.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-first-page.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-first-page.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-first-page.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-first-page.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-group-add.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-group-add.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-group-add.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-group-add.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-last-page.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-last-page.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-last-page.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-last-page.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-logout.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-logout.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-logout.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-logout.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-monitor-add.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-monitor-add.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-monitor-add.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-monitor-add.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-next-page.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-next-page.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-next-page.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-next-page.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-prev-page.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-prev-page.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-prev-page.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-prev-page.png diff --git a/guacamole-tunnel/src/main/webapp/images/action-icons/guac-user-add.png b/guacamole-auth-uds/src/main/webapp/images/action-icons/guac-user-add.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/action-icons/guac-user-add.png rename to guacamole-auth-uds/src/main/webapp/images/action-icons/guac-user-add.png diff --git a/guacamole-tunnel/src/main/webapp/images/arrows/arrows-d.png b/guacamole-auth-uds/src/main/webapp/images/arrows/arrows-d.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/arrows/arrows-d.png rename to guacamole-auth-uds/src/main/webapp/images/arrows/arrows-d.png diff --git a/guacamole-tunnel/src/main/webapp/images/arrows/arrows-l.png b/guacamole-auth-uds/src/main/webapp/images/arrows/arrows-l.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/arrows/arrows-l.png rename to guacamole-auth-uds/src/main/webapp/images/arrows/arrows-l.png diff --git a/guacamole-tunnel/src/main/webapp/images/arrows/arrows-r.png b/guacamole-auth-uds/src/main/webapp/images/arrows/arrows-r.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/arrows/arrows-r.png rename to guacamole-auth-uds/src/main/webapp/images/arrows/arrows-r.png diff --git a/guacamole-tunnel/src/main/webapp/images/arrows/arrows-u.png b/guacamole-auth-uds/src/main/webapp/images/arrows/arrows-u.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/arrows/arrows-u.png rename to guacamole-auth-uds/src/main/webapp/images/arrows/arrows-u.png diff --git a/guacamole-tunnel/src/main/webapp/images/group-icons/guac-closed.png b/guacamole-auth-uds/src/main/webapp/images/group-icons/guac-closed.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/group-icons/guac-closed.png rename to guacamole-auth-uds/src/main/webapp/images/group-icons/guac-closed.png diff --git a/guacamole-tunnel/src/main/webapp/images/group-icons/guac-open.png b/guacamole-auth-uds/src/main/webapp/images/group-icons/guac-open.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/group-icons/guac-open.png rename to guacamole-auth-uds/src/main/webapp/images/group-icons/guac-open.png diff --git a/guacamole-tunnel/src/main/webapp/images/guac-mono-192.png b/guacamole-auth-uds/src/main/webapp/images/guac-mono-192.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/guac-mono-192.png rename to guacamole-auth-uds/src/main/webapp/images/guac-mono-192.png diff --git a/guacamole-tunnel/src/main/webapp/images/guacamole-logo-144.png b/guacamole-auth-uds/src/main/webapp/images/guacamole-logo-144.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/guacamole-logo-144.png rename to guacamole-auth-uds/src/main/webapp/images/guacamole-logo-144.png diff --git a/guacamole-tunnel/src/main/webapp/images/guacamole-logo-24.png b/guacamole-auth-uds/src/main/webapp/images/guacamole-logo-24.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/guacamole-logo-24.png rename to guacamole-auth-uds/src/main/webapp/images/guacamole-logo-24.png diff --git a/guacamole-tunnel/src/main/webapp/images/guacamole-logo-64.png b/guacamole-auth-uds/src/main/webapp/images/guacamole-logo-64.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/guacamole-logo-64.png rename to guacamole-auth-uds/src/main/webapp/images/guacamole-logo-64.png diff --git a/guacamole-tunnel/src/main/webapp/images/mouse/blank.cur b/guacamole-auth-uds/src/main/webapp/images/mouse/blank.cur similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/mouse/blank.cur rename to guacamole-auth-uds/src/main/webapp/images/mouse/blank.cur diff --git a/guacamole-tunnel/src/main/webapp/images/mouse/blank.gif b/guacamole-auth-uds/src/main/webapp/images/mouse/blank.gif similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/mouse/blank.gif rename to guacamole-auth-uds/src/main/webapp/images/mouse/blank.gif diff --git a/guacamole-tunnel/src/main/webapp/images/mouse/dot.gif b/guacamole-auth-uds/src/main/webapp/images/mouse/dot.gif similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/mouse/dot.gif rename to guacamole-auth-uds/src/main/webapp/images/mouse/dot.gif diff --git a/guacamole-tunnel/src/main/webapp/images/noguacamole-logo-24.png b/guacamole-auth-uds/src/main/webapp/images/noguacamole-logo-24.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/noguacamole-logo-24.png rename to guacamole-auth-uds/src/main/webapp/images/noguacamole-logo-24.png diff --git a/guacamole-tunnel/src/main/webapp/images/progress.png b/guacamole-auth-uds/src/main/webapp/images/progress.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/progress.png rename to guacamole-auth-uds/src/main/webapp/images/progress.png diff --git a/guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-monitor.png b/guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-monitor.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-monitor.png rename to guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-monitor.png diff --git a/guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-plug.png b/guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-plug.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-plug.png rename to guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-plug.png diff --git a/guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-text.png b/guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-text.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/protocol-icons/guac-text.png rename to guacamole-auth-uds/src/main/webapp/images/protocol-icons/guac-text.png diff --git a/guacamole-tunnel/src/main/webapp/images/settings/tablet-keys.png b/guacamole-auth-uds/src/main/webapp/images/settings/tablet-keys.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/settings/tablet-keys.png rename to guacamole-auth-uds/src/main/webapp/images/settings/tablet-keys.png diff --git a/guacamole-tunnel/src/main/webapp/images/settings/touchpad.png b/guacamole-auth-uds/src/main/webapp/images/settings/touchpad.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/settings/touchpad.png rename to guacamole-auth-uds/src/main/webapp/images/settings/touchpad.png diff --git a/guacamole-tunnel/src/main/webapp/images/settings/touchscreen.png b/guacamole-auth-uds/src/main/webapp/images/settings/touchscreen.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/settings/touchscreen.png rename to guacamole-auth-uds/src/main/webapp/images/settings/touchscreen.png diff --git a/guacamole-tunnel/src/main/webapp/images/settings/zoom-in.png b/guacamole-auth-uds/src/main/webapp/images/settings/zoom-in.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/settings/zoom-in.png rename to guacamole-auth-uds/src/main/webapp/images/settings/zoom-in.png diff --git a/guacamole-tunnel/src/main/webapp/images/settings/zoom-out.png b/guacamole-auth-uds/src/main/webapp/images/settings/zoom-out.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/settings/zoom-out.png rename to guacamole-auth-uds/src/main/webapp/images/settings/zoom-out.png diff --git a/guacamole-tunnel/src/main/webapp/images/user-icons/guac-user.png b/guacamole-auth-uds/src/main/webapp/images/user-icons/guac-user.png similarity index 100% rename from guacamole-tunnel/src/main/webapp/images/user-icons/guac-user.png rename to guacamole-auth-uds/src/main/webapp/images/user-icons/guac-user.png diff --git a/guacamole-tunnel/NOTICE b/guacamole-tunnel/NOTICE deleted file mode 100644 index 2ef7e548..00000000 --- a/guacamole-tunnel/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -Apache Guacamole -Copyright 2016 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). diff --git a/guacamole-tunnel/pom.xml b/guacamole-tunnel/pom.xml deleted file mode 100644 index 4caea2fc..00000000 --- a/guacamole-tunnel/pom.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - 4.0.0 - org.openuds.server - transport - war - 2.5.0 - Guacamole Transport - https://github.com/dkmstr/openuds - - - UTF-8 - - - - transport - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.3 - - 1.6 - 1.6 - - -Xlint:all - -Werror - - true - - - - - - org.apache.maven.plugins - maven-war-plugin - 2.6 - - - - org.apache.guacamole - guacamole-common-js - zip - - - - - - - - - - - - - javax.servlet - servlet-api - provided - 2.5 - - - - - org.slf4j - slf4j-api - 1.6.1 - - - org.slf4j - slf4j-jcl - 1.6.1 - runtime - - - - - org.apache.guacamole - guacamole-common - 1.1.0 - - - - - org.apache.guacamole - guacamole-common-js - 1.2.0 - zip - runtime - - - - - javax.websocket - javax.websocket-api - 1.0 - provided - - - - - org.eclipse.jetty - jetty-websocket - 8.1.1.v20120215 - provided - - - - - org.eclipse.jetty - jetty-parent - 20 - provided - pom - - - org.eclipse.jetty.websocket - websocket-api - 9.0.7.v20131107 - provided - - - org.eclipse.jetty.websocket - websocket-servlet - 9.0.7.v20131107 - provided - - - - - org.apache.tomcat - tomcat-catalina - 7.0.37 - provided - - - org.apache.tomcat - tomcat-coyote - 7.0.37 - provided - - - - - com.sun.jersey.contribs - jersey-guice - 1.17.1 - - - - - com.google.inject.extensions - guice-servlet - 3.0 - - - - - com.google.inject - guice - 3.0 - - - - - diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelLoader.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelLoader.java deleted file mode 100644 index b91527f5..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelLoader.java +++ /dev/null @@ -1,41 +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.tunnel; - -import com.google.inject.Module; - -/** - * Generic means of loading a tunnel without adding explicit dependencies within - * the main ServletModule, as not all servlet containers may have the classes - * required by all tunnel implementations. - * - * @author Michael Jumper - */ -public interface TunnelLoader extends Module { - - /** - * Checks whether this type of tunnel is supported by the servlet container. - * - * @return true if this type of tunnel is supported and can be loaded - * without errors, false otherwise. - */ - public boolean isSupported(); - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelModule.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelModule.java deleted file mode 100644 index 9b03ecb8..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelModule.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel; - -import org.apache.guacamole.tunnel.http.RestrictedGuacamoleHTTPTunnelServlet; -import com.google.inject.servlet.ServletModule; -import java.lang.reflect.InvocationTargetException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Module which loads tunnel implementations. - * - * @author Michael Jumper - */ -public class TunnelModule extends ServletModule { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(TunnelModule.class); - - /** - * Classnames of all implementation-specific WebSocket tunnel modules. - */ - private static final String[] WEBSOCKET_MODULES = { - "org.apache.guacamole.tunnel.websocket.WebSocketTunnelModule", - "org.apache.guacamole.tunnel.websocket.jetty8.WebSocketTunnelModule", - "org.apache.guacamole.tunnel.websocket.jetty9.WebSocketTunnelModule", - "org.apache.guacamole.tunnel.websocket.tomcat.WebSocketTunnelModule" - }; - - private boolean loadWebSocketModule(String classname) { - - try { - - // Attempt to find WebSocket module - Class module = Class.forName(classname); - - // Create loader - TunnelLoader loader = (TunnelLoader) module.getConstructor().newInstance(); - - // Install module, if supported - if (loader.isSupported()) { - install(loader); - return true; - } - - } - - // If no such class or constructor, etc., then this particular - // WebSocket support is not present - catch (ClassNotFoundException e) {} - catch (NoClassDefFoundError e) {} - catch (NoSuchMethodException e) {} - - // Log errors which indicate bugs - catch (InstantiationException e) { - logger.debug("Error instantiating WebSocket module.", e); - } - catch (IllegalAccessException e) { - logger.debug("Error instantiating WebSocket module.", e); - } - catch (InvocationTargetException e) { - logger.debug("Error instantiating WebSocket module.", e); - } - - // Load attempt failed - return false; - - } - - @Override - protected void configureServlets() { - - bind(TunnelRequestService.class); - - // Set up HTTP tunnel - serve("/tunnel").with(RestrictedGuacamoleHTTPTunnelServlet.class); - - // Try to load each WebSocket tunnel in sequence - for (String classname : WEBSOCKET_MODULES) { - if (loadWebSocketModule(classname)) { - logger.debug("WebSocket module loaded: {}", classname); - return; - } - } - - // Warn of lack of WebSocket - logger.info("WebSocket support NOT present. Only HTTP will be used."); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java deleted file mode 100644 index 2c97f047..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequest.java +++ /dev/null @@ -1,371 +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.tunnel; - -import java.util.List; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleException; - -/** - * A request object which provides only the functions absolutely required to - * retrieve and connect to a tunnel. - * - * @author Michael Jumper - */ -public abstract class TunnelRequest { - - /** - * The name of the request parameter containing the user's authentication - * token. - */ - public static final String AUTH_TOKEN_PARAMETER = "token"; - - /** - * The name of the parameter containing the identifier of the - * AuthenticationProvider associated with the UserContext containing the - * object to which a tunnel is being requested. - */ - public static final String AUTH_PROVIDER_IDENTIFIER_PARAMETER = "GUAC_DATA_SOURCE"; - - /** - * The name of the parameter specifying the type of object to which a - * tunnel is being requested. Currently, this may be "c" for a Guacamole - * connection, or "g" for a Guacamole connection group. - */ - public static final String TYPE_PARAMETER = "GUAC_TYPE"; - - /** - * The name of the parameter containing the unique identifier of the object - * to which a tunnel is being requested. - */ - public static final String IDENTIFIER_PARAMETER = "GUAC_ID"; - - /** - * The name of the parameter containing the desired display width, in - * pixels. - */ - public static final String WIDTH_PARAMETER = "GUAC_WIDTH"; - - /** - * The name of the parameter containing the desired display height, in - * pixels. - */ - public static final String HEIGHT_PARAMETER = "GUAC_HEIGHT"; - - /** - * The name of the parameter containing the desired display resolution, in - * DPI. - */ - public static final String DPI_PARAMETER = "GUAC_DPI"; - - /** - * The name of the parameter specifying one supported audio mimetype. This - * will normally appear multiple times within a single tunnel request - - * once for each mimetype. - */ - public static final String AUDIO_PARAMETER = "GUAC_AUDIO"; - - /** - * The name of the parameter specifying one supported video mimetype. This - * will normally appear multiple times within a single tunnel request - - * once for each mimetype. - */ - public static final String VIDEO_PARAMETER = "GUAC_VIDEO"; - - /** - * The name of the parameter specifying one supported image mimetype. This - * will normally appear multiple times within a single tunnel request - - * once for each mimetype. - */ - public static final String IMAGE_PARAMETER = "GUAC_IMAGE"; - - /** - * All supported object types that can be used as the destination of a - * tunnel. - */ - public static enum Type { - - /** - * A Guacamole connection. - */ - CONNECTION("c"), - - /** - * A Guacamole connection group. - */ - CONNECTION_GROUP("g"); - - /** - * The parameter value which denotes a destination object of this type. - */ - final String PARAMETER_VALUE; - - /** - * Defines a Type having the given corresponding parameter value. - * - * @param value - * The parameter value which denotes a destination object of this - * type. - */ - Type(String value) { - PARAMETER_VALUE = value; - } - - }; - - /** - * Returns the value of the parameter having the given name. - * - * @param name - * The name of the parameter to return. - * - * @return - * The value of the parameter having the given name, or null if no such - * parameter was specified. - */ - public abstract String getParameter(String name); - - /** - * Returns a list of all values specified for the given parameter. - * - * @param name - * The name of the parameter to return. - * - * @return - * All values of the parameter having the given name , or null if no - * such parameter was specified. - */ - public abstract List getParameterValues(String name); - - /** - * Returns the value of the parameter having the given name, throwing an - * exception if the parameter is missing. - * - * @param name - * The name of the parameter to return. - * - * @return - * The value of the parameter having the given name. - * - * @throws GuacamoleException - * If the parameter is not present in the request. - */ - public String getRequiredParameter(String name) throws GuacamoleException { - - // Pull requested parameter, aborting if absent - String value = getParameter(name); - if (value == null) - throw new GuacamoleClientException("Parameter \"" + name + "\" is required."); - - return value; - - } - - /** - * Returns the integer value of the parameter having the given name, - * throwing an exception if the parameter cannot be parsed. - * - * @param name - * The name of the parameter to return. - * - * @return - * The integer value of the parameter having the given name, or null if - * the parameter is missing. - * - * @throws GuacamoleException - * If the parameter is not a valid integer. - */ - public Integer getIntegerParameter(String name) throws GuacamoleException { - - // Pull requested parameter - String value = getParameter(name); - if (value == null) - return null; - - // Attempt to parse as an integer - try { - return Integer.parseInt(value); - } - - // Rethrow any parsing error as a GuacamoleClientException - catch (NumberFormatException e) { - throw new GuacamoleClientException("Parameter \"" + name + "\" must be a valid integer.", e); - } - - } - - /** - * Returns the authentication token associated with this tunnel request. - * - * @return - * The authentication token associated with this tunnel request, or - * null if no authentication token is present. - */ - public String getAuthenticationToken() { - return getParameter(AUTH_TOKEN_PARAMETER); - } - - /** - * Returns the identifier of the AuthenticationProvider associated with the - * UserContext from which the connection or connection group is to be - * retrieved when the tunnel is created. In the context of the REST API and - * the JavaScript side of the web application, this is referred to as the - * data source identifier. - * - * @return - * The identifier of the AuthenticationProvider associated with the - * UserContext from which the connection or connection group is to be - * retrieved when the tunnel is created. - * - * @throws GuacamoleException - * If the identifier was not present in the request. - */ - public String getAuthenticationProviderIdentifier() - throws GuacamoleException { - return getRequiredParameter(AUTH_PROVIDER_IDENTIFIER_PARAMETER); - } - - /** - * Returns the type of object for which the tunnel is being requested. - * - * @return - * The type of object for which the tunnel is being requested. - * - * @throws GuacamoleException - * If the type was not present in the request, or if the type requested - * is in the wrong format. - */ - public Type getType() throws GuacamoleException { - - String type = getRequiredParameter(TYPE_PARAMETER); - - // For each possible object type - for (Type possibleType : Type.values()) { - - // Match against defined parameter value - if (type.equals(possibleType.PARAMETER_VALUE)) - return possibleType; - - } - - throw new GuacamoleClientException("Illegal identifier - unknown type."); - - } - - /** - * Returns the identifier of the destination of the tunnel being requested. - * As there are multiple types of destination objects available, and within - * multiple data sources, the associated object type and data source are - * also necessary to determine what this identifier refers to. - * - * @return - * The identifier of the destination of the tunnel being requested. - * - * @throws GuacamoleException - * If the identifier was not present in the request. - */ - public String getIdentifier() throws GuacamoleException { - return getRequiredParameter(IDENTIFIER_PARAMETER); - } - - /** - * Returns the display width desired for the Guacamole session over the - * tunnel being requested. - * - * @return - * The display width desired for the Guacamole session over the tunnel - * being requested, or null if no width was given. - * - * @throws GuacamoleException - * If the width specified was not a valid integer. - */ - public Integer getWidth() throws GuacamoleException { - return getIntegerParameter(WIDTH_PARAMETER); - } - - /** - * Returns the display height desired for the Guacamole session over the - * tunnel being requested. - * - * @return - * The display height desired for the Guacamole session over the tunnel - * being requested, or null if no width was given. - * - * @throws GuacamoleException - * If the height specified was not a valid integer. - */ - public Integer getHeight() throws GuacamoleException { - return getIntegerParameter(HEIGHT_PARAMETER); - } - - /** - * Returns the display resolution desired for the Guacamole session over - * the tunnel being requested, in DPI. - * - * @return - * The display resolution desired for the Guacamole session over the - * tunnel being requested, or null if no resolution was given. - * - * @throws GuacamoleException - * If the resolution specified was not a valid integer. - */ - public Integer getDPI() throws GuacamoleException { - return getIntegerParameter(DPI_PARAMETER); - } - - /** - * Returns a list of all audio mimetypes declared as supported within the - * tunnel request. - * - * @return - * A list of all audio mimetypes declared as supported within the - * tunnel request, or null if no mimetypes were specified. - */ - public List getAudioMimetypes() { - return getParameterValues(AUDIO_PARAMETER); - } - - /** - * Returns a list of all video mimetypes declared as supported within the - * tunnel request. - * - * @return - * A list of all video mimetypes declared as supported within the - * tunnel request, or null if no mimetypes were specified. - */ - public List getVideoMimetypes() { - return getParameterValues(VIDEO_PARAMETER); - } - - /** - * Returns a list of all image mimetypes declared as supported within the - * tunnel request. - * - * @return - * A list of all image mimetypes declared as supported within the - * tunnel request, or null if no mimetypes were specified. - */ - public List getImageMimetypes() { - return getParameterValues(IMAGE_PARAMETER); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java deleted file mode 100644 index 0cb62ee0..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import java.util.List; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleSocket; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.net.InetGuacamoleSocket; -import org.apache.guacamole.net.SimpleGuacamoleTunnel; -import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; -import org.apache.guacamole.protocol.GuacamoleClientInformation; -import org.apache.guacamole.protocol.GuacamoleConfiguration; -import org.openuds.guacamole.connection.ConnectionService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utility class that takes a standard request from the Guacamole JavaScript - * client and produces the corresponding GuacamoleTunnel. The implementation - * of this utility is specific to the form of request used by the upstream - * Guacamole web application, and is not necessarily useful to applications - * that use purely the Guacamole API. - * - * @author Michael Jumper - * @author Vasily Loginov - */ -@Singleton -public class TunnelRequestService { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(TunnelRequestService.class); - - /** - * Service for retrieving remotely-maintained connection information. - */ - @Inject - private ConnectionService connectionService; - - /** - * The hostname of the server hosting guacd. - */ - private static final String GUACD_HOSTNAME = "127.0.0.1"; - - /** - * The port that guacd will be listening on. - */ - private static final int GUACD_PORT = 4822; - - /** - * Creates a new tunnel using the parameters and credentials present in - * the given request. - * - * @param request - * The HttpServletRequest describing the tunnel to create. - * - * @return - * The created tunnel, or null if the tunnel could not be created. - * - * @throws GuacamoleException - * If an error occurs while creating the tunnel. - */ - public GuacamoleTunnel createTunnel(TunnelRequest request) throws GuacamoleException { - - // Pull OpenUDS-specific "data" parameter - String data = request.getParameter("data"); - if (data == null || data.isEmpty()) { - logger.debug("No ID received in tunnel connect request."); - throw new GuacamoleClientException("Connection data not provided."); - } - - logger.debug("Establishing tunnel and connection with data from \"{}\"...", data); - - // Get connection from remote service - GuacamoleConfiguration config = connectionService.getConnectionConfiguration(data); - if (config == null) - throw new GuacamoleClientException("Connection configuration could not be retrieved."); - - // Get client information - GuacamoleClientInformation info = new GuacamoleClientInformation(); - - // Set width if provided - String width = request.getParameter("GUAC_WIDTH"); - if (width != null) - info.setOptimalScreenWidth(Integer.parseInt(width)); - - // Set height if provided - String height = request.getParameter("GUAC_HEIGHT"); - if (height != null) - info.setOptimalScreenHeight(Integer.parseInt(height)); - - // Set resolution if provided - String dpi = request.getParameter("GUAC_DPI"); - if (dpi != null) - info.setOptimalResolution(Integer.parseInt(dpi)); - - // Add audio mimetypes - List audio_mimetypes = request.getParameterValues("GUAC_AUDIO"); - if (audio_mimetypes != null) - info.getAudioMimetypes().addAll(audio_mimetypes); - - // Add video mimetypes - List video_mimetypes = request.getParameterValues("GUAC_VIDEO"); - if (video_mimetypes != null) - info.getVideoMimetypes().addAll(video_mimetypes); - - // Add image mimetypes - List image_mimetypes = request.getParameterValues("GUAC_IMAGE"); - if (image_mimetypes != null) - info.getImageMimetypes().addAll(image_mimetypes); - - // Connect socket for connection - GuacamoleSocket socket; - try { - socket = new ConfiguredGuacamoleSocket( - new InetGuacamoleSocket(GUACD_HOSTNAME, GUACD_PORT), - config, info - ); - } - - // Log any errors during connection - catch (GuacamoleException e) { - logger.error("Unable to connect to guacd.", e); - throw e; - } - - // Return corresponding tunnel - return new SimpleGuacamoleTunnel(socket); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java deleted file mode 100644 index 2b88af93..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/HTTPTunnelRequest.java +++ /dev/null @@ -1,88 +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.tunnel.http; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.tunnel.TunnelRequest; - -/** - * HTTP-specific implementation of TunnelRequest. - * - * @author Michael Jumper - */ -public class HTTPTunnelRequest extends TunnelRequest { - - /** - * A copy of the parameters obtained from the HttpServletRequest used to - * construct the HTTPTunnelRequest. - */ - private final Map> parameterMap = - new HashMap>(); - - /** - * Creates a HTTPTunnelRequest which copies and exposes the parameters - * from the given HttpServletRequest. - * - * @param request - * The HttpServletRequest to copy parameter values from. - */ - @SuppressWarnings("unchecked") // getParameterMap() is defined as returning Map - public HTTPTunnelRequest(HttpServletRequest request) { - - // For each parameter - for (Map.Entry mapEntry : ((Map) - request.getParameterMap()).entrySet()) { - - // Get parameter name and corresponding values - String parameterName = mapEntry.getKey(); - List parameterValues = Arrays.asList(mapEntry.getValue()); - - // Store copy of all values in our own map - parameterMap.put( - parameterName, - new ArrayList(parameterValues) - ); - - } - - } - - @Override - public String getParameter(String name) { - List values = getParameterValues(name); - - // Return the first value from the list if available - if (values != null && !values.isEmpty()) - return values.get(0); - - return null; - } - - @Override - public List getParameterValues(String name) { - return parameterMap.get(name); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java deleted file mode 100644 index 50f6df65..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/RestrictedGuacamoleHTTPTunnelServlet.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.http; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet; -import org.apache.guacamole.tunnel.TunnelRequestService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Connects users to a tunnel associated with the authorized connection - * having the given ID. - * - * @author Michael Jumper - */ -@Singleton -public class RestrictedGuacamoleHTTPTunnelServlet extends GuacamoleHTTPTunnelServlet { - - /** - * Service for handling tunnel requests. - */ - @Inject - private TunnelRequestService tunnelRequestService; - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleHTTPTunnelServlet.class); - - @Override - protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException { - - // Attempt to create HTTP tunnel - GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request)); - - // If successful, warn of lack of WebSocket - logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal."); - - return tunnel; - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/package-info.java deleted file mode 100644 index a7cb73ef..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/http/package-info.java +++ /dev/null @@ -1,23 +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. - */ - -/** - * Classes which leverage Guacamole's built-in HTTP tunnel implementation. - */ -package org.apache.guacamole.tunnel.http; diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/package-info.java deleted file mode 100644 index 383b4b6b..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/package-info.java +++ /dev/null @@ -1,23 +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. - */ - -/** - * Classes which are common to all tunnel implementations. - */ -package org.apache.guacamole.tunnel; diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java deleted file mode 100644 index fd2c022c..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/RestrictedGuacamoleWebSocketTunnelEndpoint.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket; - -import com.google.inject.Provider; -import java.util.Map; -import javax.websocket.EndpointConfig; -import javax.websocket.HandshakeResponse; -import javax.websocket.Session; -import javax.websocket.server.HandshakeRequest; -import javax.websocket.server.ServerEndpointConfig; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.tunnel.TunnelRequest; -import org.apache.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * Tunnel implementation which uses WebSocket as a tunnel backend, rather than - * HTTP, properly parsing connection IDs included in the connection request. - */ -public class RestrictedGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTunnelEndpoint { - - /** - * Unique string which shall be used to store the TunnelRequest - * associated with a WebSocket connection. - */ - private static final String TUNNEL_REQUEST_PROPERTY = "WS_GUAC_TUNNEL_REQUEST"; - - /** - * Unique string which shall be used to store the TunnelRequestService to - * be used for processing TunnelRequests. - */ - private static final String TUNNEL_REQUEST_SERVICE_PROPERTY = "WS_GUAC_TUNNEL_REQUEST_SERVICE"; - - /** - * Configurator implementation which stores the requested GuacamoleTunnel - * within the user properties. The GuacamoleTunnel will be later retrieved - * during the connection process. - */ - public static class Configurator extends ServerEndpointConfig.Configurator { - - /** - * Provider which provides instances of a service for handling - * tunnel requests. - */ - private final Provider tunnelRequestServiceProvider; - - /** - * Creates a new Configurator which uses the given tunnel request - * service provider to retrieve the necessary service to handle new - * connections requests. - * - * @param tunnelRequestServiceProvider - * The tunnel request service provider to use for all new - * connections. - */ - public Configurator(Provider tunnelRequestServiceProvider) { - this.tunnelRequestServiceProvider = tunnelRequestServiceProvider; - } - - @Override - public void modifyHandshake(ServerEndpointConfig config, - HandshakeRequest request, HandshakeResponse response) { - - super.modifyHandshake(config, request, response); - - // Store tunnel request and tunnel request service for retrieval - // upon WebSocket open - Map userProperties = config.getUserProperties(); - userProperties.clear(); - userProperties.put(TUNNEL_REQUEST_PROPERTY, new WebSocketTunnelRequest(request)); - userProperties.put(TUNNEL_REQUEST_SERVICE_PROPERTY, tunnelRequestServiceProvider.get()); - - } - - } - - @Override - protected GuacamoleTunnel createTunnel(Session session, - EndpointConfig config) throws GuacamoleException { - - Map userProperties = config.getUserProperties(); - - // Get original tunnel request - TunnelRequest tunnelRequest = (TunnelRequest) userProperties.get(TUNNEL_REQUEST_PROPERTY); - if (tunnelRequest == null) - return null; - - // Get tunnel request service - TunnelRequestService tunnelRequestService = (TunnelRequestService) userProperties.get(TUNNEL_REQUEST_SERVICE_PROPERTY); - if (tunnelRequestService == null) - return null; - - // Create and return tunnel - return tunnelRequestService.createTunnel(tunnelRequest); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java deleted file mode 100644 index b4ece2d0..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelModule.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket; - -import com.google.inject.Provider; -import com.google.inject.servlet.ServletModule; -import java.util.Arrays; -import javax.websocket.DeploymentException; -import javax.websocket.server.ServerContainer; -import javax.websocket.server.ServerEndpointConfig; -import org.apache.guacamole.tunnel.TunnelLoader; -import org.apache.guacamole.tunnel.TunnelRequestService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loads the JSR-356 WebSocket tunnel implementation. - * - * @author Michael Jumper - */ -public class WebSocketTunnelModule extends ServletModule implements TunnelLoader { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class); - - @Override - public boolean isSupported() { - - try { - - // Attempt to find WebSocket servlet - Class.forName("javax.websocket.Endpoint"); - - // Support found - return true; - - } - - // If no such servlet class, this particular WebSocket support - // is not present - catch (ClassNotFoundException e) {} - catch (NoClassDefFoundError e) {} - - // Support not found - return false; - - } - - @Override - public void configureServlets() { - - logger.info("Loading JSR-356 WebSocket support..."); - - // Get container - ServerContainer container = (ServerContainer) getServletContext().getAttribute("javax.websocket.server.ServerContainer"); - if (container == null) { - logger.warn("ServerContainer attribute required by JSR-356 is missing. Cannot load JSR-356 WebSocket support."); - return; - } - - Provider tunnelRequestServiceProvider = getProvider(TunnelRequestService.class); - - // Build configuration for WebSocket tunnel - ServerEndpointConfig config = - ServerEndpointConfig.Builder.create(RestrictedGuacamoleWebSocketTunnelEndpoint.class, "/websocket-tunnel") - .configurator(new RestrictedGuacamoleWebSocketTunnelEndpoint.Configurator(tunnelRequestServiceProvider)) - .subprotocols(Arrays.asList(new String[]{"guacamole"})) - .build(); - - try { - - // Add configuration to container - container.addEndpoint(config); - - } - catch (DeploymentException e) { - logger.error("Unable to deploy WebSocket tunnel.", e); - } - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java deleted file mode 100644 index acebe057..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/WebSocketTunnelRequest.java +++ /dev/null @@ -1,67 +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.tunnel.websocket; - -import java.util.List; -import java.util.Map; -import javax.websocket.server.HandshakeRequest; -import org.apache.guacamole.tunnel.TunnelRequest; - -/** - * WebSocket-specific implementation of TunnelRequest. - * - * @author Michael Jumper - */ -public class WebSocketTunnelRequest extends TunnelRequest { - - /** - * All parameters passed via HTTP to the WebSocket handshake. - */ - private final Map> handshakeParameters; - - /** - * Creates a TunnelRequest implementation which delegates parameter and - * session retrieval to the given HandshakeRequest. - * - * @param request The HandshakeRequest to wrap. - */ - public WebSocketTunnelRequest(HandshakeRequest request) { - this.handshakeParameters = request.getParameterMap(); - } - - @Override - public String getParameter(String name) { - - // Pull list of values, if present - List values = getParameterValues(name); - if (values == null || values.isEmpty()) - return null; - - // Return first parameter value arbitrarily - return values.get(0); - - } - - @Override - public List getParameterValues(String name) { - return handshakeParameters.get(name); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java deleted file mode 100644 index 933ff654..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/GuacamoleWebSocketTunnelServlet.java +++ /dev/null @@ -1,236 +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.tunnel.websocket.jetty8; - -import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.io.GuacamoleReader; -import org.apache.guacamole.io.GuacamoleWriter; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.eclipse.jetty.websocket.WebSocket; -import org.eclipse.jetty.websocket.WebSocket.Connection; -import org.eclipse.jetty.websocket.WebSocketServlet; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleConnectionClosedException; -import org.apache.guacamole.protocol.GuacamoleInstruction; -import org.apache.guacamole.tunnel.http.HTTPTunnelRequest; -import org.apache.guacamole.tunnel.TunnelRequest; -import org.apache.guacamole.protocol.GuacamoleStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet. - * - * @author Michael Jumper - */ -public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class); - - /** - * The default, minimum buffer size for instructions. - */ - private static final int BUFFER_SIZE = 8192; - - /** - * Sends the given status on the given WebSocket connection and closes the - * connection. - * - * @param connection The WebSocket connection to close. - * @param guac_status The status to send. - */ - public static void closeConnection(Connection connection, - GuacamoleStatus guac_status) { - - connection.close(guac_status.getWebSocketCode(), - Integer.toString(guac_status.getGuacamoleStatusCode())); - - } - - @Override - public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { - - final TunnelRequest tunnelRequest = new HTTPTunnelRequest(request); - - // Return new WebSocket which communicates through tunnel - return new WebSocket.OnTextMessage() { - - /** - * The GuacamoleTunnel associated with the connected WebSocket. If - * the WebSocket has not yet been connected, this will be null. - */ - private GuacamoleTunnel tunnel = null; - - @Override - public void onMessage(String string) { - - // Ignore inbound messages if there is no associated tunnel - if (tunnel == null) - return; - - GuacamoleWriter writer = tunnel.acquireWriter(); - - // Write message received - try { - writer.write(string.toCharArray()); - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - } - catch (GuacamoleException e) { - logger.debug("WebSocket tunnel write failed.", e); - } - - tunnel.releaseWriter(); - - } - - @Override - public void onOpen(final Connection connection) { - - try { - tunnel = doConnect(tunnelRequest); - } - catch (GuacamoleException e) { - logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage()); - logger.debug("Error connecting WebSocket tunnel.", e); - closeConnection(connection, e.getStatus()); - return; - } - - // Do not start connection if tunnel does not exist - if (tunnel == null) { - closeConnection(connection, GuacamoleStatus.RESOURCE_NOT_FOUND); - return; - } - - Thread readThread = new Thread() { - - @Override - public void run() { - - StringBuilder buffer = new StringBuilder(BUFFER_SIZE); - GuacamoleReader reader = tunnel.acquireReader(); - char[] readMessage; - - try { - - // Send tunnel UUID - connection.sendMessage(new GuacamoleInstruction( - GuacamoleTunnel.INTERNAL_DATA_OPCODE, - tunnel.getUUID().toString() - ).toString()); - - try { - - // Attempt to read - while ((readMessage = reader.read()) != null) { - - // Buffer message - buffer.append(readMessage); - - // Flush if we expect to wait or buffer is getting full - if (!reader.available() || buffer.length() >= BUFFER_SIZE) { - connection.sendMessage(buffer.toString()); - buffer.setLength(0); - } - - } - - // No more data - closeConnection(connection, GuacamoleStatus.SUCCESS); - - } - - // Catch any thrown guacamole exception and attempt - // to pass within the WebSocket connection, logging - // each error appropriately. - catch (GuacamoleClientException e) { - logger.info("WebSocket connection terminated: {}", e.getMessage()); - logger.debug("WebSocket connection terminated due to client error.", e); - closeConnection(connection, e.getStatus()); - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - closeConnection(connection, GuacamoleStatus.SUCCESS); - } - catch (GuacamoleException e) { - logger.error("Connection to guacd terminated abnormally: {}", e.getMessage()); - logger.debug("Internal error during connection to guacd.", e); - closeConnection(connection, e.getStatus()); - } - - } - catch (IOException e) { - logger.debug("WebSocket tunnel read failed due to I/O error.", e); - } - - } - - }; - - readThread.start(); - - } - - @Override - public void onClose(int i, String string) { - try { - if (tunnel != null) - tunnel.close(); - } - catch (GuacamoleException e) { - logger.debug("Unable to close connection to guacd.", e); - } - } - - }; - - } - - /** - * Called whenever the JavaScript Guacamole client makes a connection - * request. It it up to the implementor of this function to define what - * conditions must be met for a tunnel to be configured and returned as a - * result of this connection request (whether some sort of credentials must - * be specified, for example). - * - * @param request - * The TunnelRequest associated with the connection request received. - * Any parameters specified along with the connection request can be - * read from this object. - * - * @return - * A newly constructed GuacamoleTunnel if successful, null otherwise. - * - * @throws GuacamoleException - * If an error occurs while constructing the GuacamoleTunnel, or if the - * conditions required for connection are not met. - */ - protected abstract GuacamoleTunnel doConnect(TunnelRequest request) - throws GuacamoleException; - -} - diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java deleted file mode 100644 index 267fa136..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/RestrictedGuacamoleWebSocketTunnelServlet.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket.jetty8; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.tunnel.TunnelRequest; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * Tunnel servlet implementation which uses WebSocket as a tunnel backend, - * rather than HTTP, properly parsing connection IDs included in the connection - * request. - */ -@Singleton -public class RestrictedGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet { - - /** - * Service for handling tunnel requests. - */ - @Inject - private TunnelRequestService tunnelRequestService; - - @Override - protected GuacamoleTunnel doConnect(TunnelRequest request) - throws GuacamoleException { - return tunnelRequestService.createTunnel(request); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java deleted file mode 100644 index 4cbc2b09..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/WebSocketTunnelModule.java +++ /dev/null @@ -1,70 +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.tunnel.websocket.jetty8; - -import com.google.inject.servlet.ServletModule; -import org.apache.guacamole.tunnel.TunnelLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loads the Jetty 8 WebSocket tunnel implementation. - * - * @author Michael Jumper - */ -public class WebSocketTunnelModule extends ServletModule implements TunnelLoader { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class); - - @Override - public boolean isSupported() { - - try { - - // Attempt to find WebSocket servlet - Class.forName("org.apache.guacamole.tunnel.websocket.jetty8.RestrictedGuacamoleWebSocketTunnelServlet"); - - // Support found - return true; - - } - - // If no such servlet class, this particular WebSocket support - // is not present - catch (ClassNotFoundException e) {} - catch (NoClassDefFoundError e) {} - - // Support not found - return false; - - } - - @Override - public void configureServlets() { - - logger.info("Loading Jetty 8 WebSocket support..."); - serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java deleted file mode 100644 index 9aecac2b..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty8/package-info.java +++ /dev/null @@ -1,23 +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. - */ - -/** - * Jetty 8 WebSocket tunnel implementation. The classes here require Jetty 8. - */ -package org.apache.guacamole.tunnel.websocket.jetty8; diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java deleted file mode 100644 index 89105fc9..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/GuacamoleWebSocketTunnelListener.java +++ /dev/null @@ -1,247 +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.tunnel.websocket.jetty9; - -import java.io.IOException; -import org.eclipse.jetty.websocket.api.CloseStatus; -import org.eclipse.jetty.websocket.api.RemoteEndpoint; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.WebSocketListener; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleConnectionClosedException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.io.GuacamoleReader; -import org.apache.guacamole.io.GuacamoleWriter; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.protocol.GuacamoleInstruction; -import org.apache.guacamole.protocol.GuacamoleStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * WebSocket listener implementation which provides a Guacamole tunnel - * - * @author Michael Jumper - */ -public abstract class GuacamoleWebSocketTunnelListener implements WebSocketListener { - - /** - * The default, minimum buffer size for instructions. - */ - private static final int BUFFER_SIZE = 8192; - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleWebSocketTunnelServlet.class); - - /** - * The underlying GuacamoleTunnel. WebSocket reads/writes will be handled - * as reads/writes to this tunnel. - */ - private GuacamoleTunnel tunnel; - - /** - * Sends the given status on the given WebSocket connection and closes the - * connection. - * - * @param session The outbound WebSocket connection to close. - * @param guac_status The status to send. - */ - private void closeConnection(Session session, GuacamoleStatus guac_status) { - - try { - int code = guac_status.getWebSocketCode(); - String message = Integer.toString(guac_status.getGuacamoleStatusCode()); - session.close(new CloseStatus(code, message)); - } - catch (IOException e) { - logger.debug("Unable to close WebSocket connection.", e); - } - - } - - /** - * Returns a new tunnel for the given session. How this tunnel is created - * or retrieved is implementation-dependent. - * - * @param session The session associated with the active WebSocket - * connection. - * @return A connected tunnel, or null if no such tunnel exists. - * @throws GuacamoleException If an error occurs while retrieving the - * tunnel, or if access to the tunnel is denied. - */ - protected abstract GuacamoleTunnel createTunnel(Session session) - throws GuacamoleException; - - @Override - public void onWebSocketConnect(final Session session) { - - try { - - // Get tunnel - tunnel = createTunnel(session); - if (tunnel == null) { - closeConnection(session, GuacamoleStatus.RESOURCE_NOT_FOUND); - return; - } - - } - catch (GuacamoleException e) { - logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage()); - logger.debug("Error connecting WebSocket tunnel.", e); - closeConnection(session, e.getStatus()); - return; - } - - // Prepare read transfer thread - Thread readThread = new Thread() { - - /** - * Remote (client) side of this connection - */ - private final RemoteEndpoint remote = session.getRemote(); - - @Override - public void run() { - - StringBuilder buffer = new StringBuilder(BUFFER_SIZE); - GuacamoleReader reader = tunnel.acquireReader(); - char[] readMessage; - - try { - - // Send tunnel UUID - remote.sendString(new GuacamoleInstruction( - GuacamoleTunnel.INTERNAL_DATA_OPCODE, - tunnel.getUUID().toString() - ).toString()); - - try { - - // Attempt to read - while ((readMessage = reader.read()) != null) { - - // Buffer message - buffer.append(readMessage); - - // Flush if we expect to wait or buffer is getting full - if (!reader.available() || buffer.length() >= BUFFER_SIZE) { - remote.sendString(buffer.toString()); - buffer.setLength(0); - } - - } - - // No more data - closeConnection(session, GuacamoleStatus.SUCCESS); - - } - - // Catch any thrown guacamole exception and attempt - // to pass within the WebSocket connection, logging - // each error appropriately. - catch (GuacamoleClientException e) { - logger.info("WebSocket connection terminated: {}", e.getMessage()); - logger.debug("WebSocket connection terminated due to client error.", e); - closeConnection(session, e.getStatus()); - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - closeConnection(session, GuacamoleStatus.SUCCESS); - } - catch (GuacamoleException e) { - logger.error("Connection to guacd terminated abnormally: {}", e.getMessage()); - logger.debug("Internal error during connection to guacd.", e); - closeConnection(session, e.getStatus()); - } - - } - catch (IOException e) { - logger.debug("I/O error prevents further reads.", e); - } - - } - - }; - - readThread.start(); - - } - - @Override - public void onWebSocketText(String message) { - - // Ignore inbound messages if there is no associated tunnel - if (tunnel == null) - return; - - GuacamoleWriter writer = tunnel.acquireWriter(); - - try { - // Write received message - writer.write(message.toCharArray()); - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - } - catch (GuacamoleException e) { - logger.debug("WebSocket tunnel write failed.", e); - } - - tunnel.releaseWriter(); - - } - - @Override - public void onWebSocketBinary(byte[] payload, int offset, int length) { - throw new UnsupportedOperationException("Binary WebSocket messages are not supported."); - } - - @Override - public void onWebSocketError(Throwable t) { - - logger.debug("WebSocket tunnel closing due to error.", t); - - try { - if (tunnel != null) - tunnel.close(); - } - catch (GuacamoleException e) { - logger.debug("Unable to close connection to guacd.", e); - } - - } - - - @Override - public void onWebSocketClose(int statusCode, String reason) { - - try { - if (tunnel != null) - tunnel.close(); - } - catch (GuacamoleException e) { - logger.debug("Unable to close connection to guacd.", e); - } - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java deleted file mode 100644 index 5d3543b1..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketCreator.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket.jetty9; - -import org.eclipse.jetty.websocket.api.UpgradeRequest; -import org.eclipse.jetty.websocket.api.UpgradeResponse; -import org.eclipse.jetty.websocket.servlet.WebSocketCreator; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * WebSocketCreator which selects the appropriate WebSocketListener - * implementation if the "guacamole" subprotocol is in use. - * - * @author Michael Jumper - */ -public class RestrictedGuacamoleWebSocketCreator implements WebSocketCreator { - - /** - * Service for handling tunnel requests. - */ - private final TunnelRequestService tunnelRequestService; - - /** - * Creates a new WebSocketCreator which uses the given TunnelRequestService - * to create new GuacamoleTunnels for inbound requests. - * - * @param tunnelRequestService The service to use for inbound tunnel - * requests. - */ - public RestrictedGuacamoleWebSocketCreator(TunnelRequestService tunnelRequestService) { - this.tunnelRequestService = tunnelRequestService; - } - - @Override - public Object createWebSocket(UpgradeRequest request, UpgradeResponse response) { - - // Validate and use "guacamole" subprotocol - for (String subprotocol : request.getSubProtocols()) { - - if ("guacamole".equals(subprotocol)) { - response.setAcceptedSubProtocol(subprotocol); - return new RestrictedGuacamoleWebSocketTunnelListener(tunnelRequestService); - } - - } - - // Invalid protocol - return null; - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java deleted file mode 100644 index 338413db..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelListener.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket.jetty9; - -import org.eclipse.jetty.websocket.api.Session; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * WebSocket listener implementation which properly parses connection IDs - * included in the connection request. - * - * @author Michael Jumper - */ -public class RestrictedGuacamoleWebSocketTunnelListener extends GuacamoleWebSocketTunnelListener { - - /** - * Service for handling tunnel requests. - */ - private final TunnelRequestService tunnelRequestService; - - /** - * Creates a new WebSocketListener which uses the given TunnelRequestService - * to create new GuacamoleTunnels for inbound requests. - * - * @param tunnelRequestService The service to use for inbound tunnel - * requests. - */ - public RestrictedGuacamoleWebSocketTunnelListener(TunnelRequestService tunnelRequestService) { - this.tunnelRequestService = tunnelRequestService; - } - - @Override - protected GuacamoleTunnel createTunnel(Session session) throws GuacamoleException { - return tunnelRequestService.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest())); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java deleted file mode 100644 index 9829ac3a..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/RestrictedGuacamoleWebSocketTunnelServlet.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket.jetty9; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import org.eclipse.jetty.websocket.servlet.WebSocketServlet; -import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet. - * - * @author Michael Jumper - */ -@Singleton -public class RestrictedGuacamoleWebSocketTunnelServlet extends WebSocketServlet { - - /** - * Service for handling tunnel requests. - */ - @Inject - private TunnelRequestService tunnelRequestService; - - @Override - public void configure(WebSocketServletFactory factory) { - - // Register WebSocket implementation - factory.setCreator(new RestrictedGuacamoleWebSocketCreator(tunnelRequestService)); - - } - -} - diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java deleted file mode 100644 index c98d1cb7..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelModule.java +++ /dev/null @@ -1,70 +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.tunnel.websocket.jetty9; - -import com.google.inject.servlet.ServletModule; -import org.apache.guacamole.tunnel.TunnelLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loads the Jetty 9 WebSocket tunnel implementation. - * - * @author Michael Jumper - */ -public class WebSocketTunnelModule extends ServletModule implements TunnelLoader { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class); - - @Override - public boolean isSupported() { - - try { - - // Attempt to find WebSocket servlet - Class.forName("org.apache.guacamole.tunnel.websocket.jetty9.RestrictedGuacamoleWebSocketTunnelServlet"); - - // Support found - return true; - - } - - // If no such servlet class, this particular WebSocket support - // is not present - catch (ClassNotFoundException e) {} - catch (NoClassDefFoundError e) {} - - // Support not found - return false; - - } - - @Override - public void configureServlets() { - - logger.info("Loading Jetty 9 WebSocket support..."); - serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java deleted file mode 100644 index adbc8d01..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/WebSocketTunnelRequest.java +++ /dev/null @@ -1,73 +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.tunnel.websocket.jetty9; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.eclipse.jetty.websocket.api.UpgradeRequest; -import org.apache.guacamole.tunnel.TunnelRequest; - -/** - * Jetty 9 WebSocket-specific implementation of TunnelRequest. - * - * @author Michael Jumper - */ -public class WebSocketTunnelRequest extends TunnelRequest { - - /** - * All parameters passed via HTTP to the WebSocket handshake. - */ - private final Map handshakeParameters; - - /** - * Creates a TunnelRequest implementation which delegates parameter and - * session retrieval to the given UpgradeRequest. - * - * @param request The UpgradeRequest to wrap. - */ - public WebSocketTunnelRequest(UpgradeRequest request) { - this.handshakeParameters = request.getParameterMap(); - } - - @Override - public String getParameter(String name) { - - // Pull list of values, if present - List values = getParameterValues(name); - if (values == null || values.isEmpty()) - return null; - - // Return first parameter value arbitrarily - return values.get(0); - - } - - @Override - public List getParameterValues(String name) { - - String[] values = handshakeParameters.get(name); - if (values == null) - return null; - - return Arrays.asList(values); - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java deleted file mode 100644 index c2a5803f..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/jetty9/package-info.java +++ /dev/null @@ -1,24 +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. - */ - -/** - * Jetty 9 WebSocket tunnel implementation. The classes here require at least - * Jetty 9, prior to Jetty 9.1 (when support for JSR 356 was implemented). - */ -package org.apache.guacamole.tunnel.websocket.jetty9; diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java deleted file mode 100644 index 1d083edd..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/package-info.java +++ /dev/null @@ -1,24 +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. - */ - -/** - * Standard WebSocket tunnel implementation. The classes here require a recent - * servlet container that supports JSR 356. - */ -package org.apache.guacamole.tunnel.websocket; diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java deleted file mode 100644 index 1b9098f9..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/GuacamoleWebSocketTunnelServlet.java +++ /dev/null @@ -1,269 +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.tunnel.websocket.tomcat; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.util.List; -import javax.servlet.http.HttpServletRequest; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.io.GuacamoleReader; -import org.apache.guacamole.io.GuacamoleWriter; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.catalina.websocket.StreamInbound; -import org.apache.catalina.websocket.WebSocketServlet; -import org.apache.catalina.websocket.WsOutbound; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleConnectionClosedException; -import org.apache.guacamole.protocol.GuacamoleInstruction; -import org.apache.guacamole.tunnel.http.HTTPTunnelRequest; -import org.apache.guacamole.tunnel.TunnelRequest; -import org.apache.guacamole.protocol.GuacamoleStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A WebSocketServlet partial re-implementation of GuacamoleTunnelServlet. - * - * @author Michael Jumper - */ -public abstract class GuacamoleWebSocketTunnelServlet extends WebSocketServlet { - - /** - * The default, minimum buffer size for instructions. - */ - private static final int BUFFER_SIZE = 8192; - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(GuacamoleWebSocketTunnelServlet.class); - - /** - * Sends the given status on the given WebSocket connection and closes the - * connection. - * - * @param outbound The outbound WebSocket connection to close. - * @param guac_status The status to send. - */ - public void closeConnection(WsOutbound outbound, GuacamoleStatus guac_status) { - - try { - byte[] message = Integer.toString(guac_status.getGuacamoleStatusCode()).getBytes("UTF-8"); - outbound.close(guac_status.getWebSocketCode(), ByteBuffer.wrap(message)); - } - catch (IOException e) { - logger.debug("Unable to close WebSocket tunnel.", e); - } - - } - - @Override - protected String selectSubProtocol(List subProtocols) { - - // Search for expected protocol - for (String protocol : subProtocols) - if ("guacamole".equals(protocol)) - return "guacamole"; - - // Otherwise, fail - return null; - - } - - @Override - public StreamInbound createWebSocketInbound(String protocol, - HttpServletRequest request) { - - final TunnelRequest tunnelRequest = new HTTPTunnelRequest(request); - - // Return new WebSocket which communicates through tunnel - return new StreamInbound() { - - /** - * The GuacamoleTunnel associated with the connected WebSocket. If - * the WebSocket has not yet been connected, this will be null. - */ - private GuacamoleTunnel tunnel = null; - - @Override - protected void onTextData(Reader reader) throws IOException { - - // Ignore inbound messages if there is no associated tunnel - if (tunnel == null) - return; - - GuacamoleWriter writer = tunnel.acquireWriter(); - - // Write all available data - try { - - char[] buffer = new char[BUFFER_SIZE]; - - int num_read; - while ((num_read = reader.read(buffer)) > 0) - writer.write(buffer, 0, num_read); - - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - } - catch (GuacamoleException e) { - logger.debug("WebSocket tunnel write failed.", e); - } - - tunnel.releaseWriter(); - } - - @Override - public void onOpen(final WsOutbound outbound) { - - try { - tunnel = doConnect(tunnelRequest); - } - catch (GuacamoleException e) { - logger.error("Creation of WebSocket tunnel to guacd failed: {}", e.getMessage()); - logger.debug("Error connecting WebSocket tunnel.", e); - closeConnection(outbound, e.getStatus()); - return; - } - - // Do not start connection if tunnel does not exist - if (tunnel == null) { - closeConnection(outbound, GuacamoleStatus.RESOURCE_NOT_FOUND); - return; - } - - Thread readThread = new Thread() { - - @Override - public void run() { - - StringBuilder buffer = new StringBuilder(BUFFER_SIZE); - GuacamoleReader reader = tunnel.acquireReader(); - char[] readMessage; - - try { - - // Send tunnel UUID - outbound.writeTextMessage(CharBuffer.wrap(new GuacamoleInstruction( - GuacamoleTunnel.INTERNAL_DATA_OPCODE, - tunnel.getUUID().toString() - ).toString())); - - try { - - // Attempt to read - while ((readMessage = reader.read()) != null) { - - // Buffer message - buffer.append(readMessage); - - // Flush if we expect to wait or buffer is getting full - if (!reader.available() || buffer.length() >= BUFFER_SIZE) { - outbound.writeTextMessage(CharBuffer.wrap(buffer)); - buffer.setLength(0); - } - - } - - // No more data - closeConnection(outbound, GuacamoleStatus.SUCCESS); - - } - - // Catch any thrown guacamole exception and attempt - // to pass within the WebSocket connection, logging - // each error appropriately. - catch (GuacamoleClientException e) { - logger.info("WebSocket connection terminated: {}", e.getMessage()); - logger.debug("WebSocket connection terminated due to client error.", e); - closeConnection(outbound, e.getStatus()); - } - catch (GuacamoleConnectionClosedException e) { - logger.debug("Connection to guacd closed.", e); - closeConnection(outbound, GuacamoleStatus.SUCCESS); - } - catch (GuacamoleException e) { - logger.error("Connection to guacd terminated abnormally: {}", e.getMessage()); - logger.debug("Internal error during connection to guacd.", e); - closeConnection(outbound, e.getStatus()); - } - - } - catch (IOException e) { - logger.debug("I/O error prevents further reads.", e); - } - - } - - }; - - readThread.start(); - - } - - @Override - public void onClose(int i) { - try { - if (tunnel != null) - tunnel.close(); - } - catch (GuacamoleException e) { - logger.debug("Unable to close connection to guacd.", e); - } - } - - @Override - protected void onBinaryData(InputStream in) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); - } - - }; - - } - - /** - * Called whenever the JavaScript Guacamole client makes a connection - * request. It it up to the implementor of this function to define what - * conditions must be met for a tunnel to be configured and returned as a - * result of this connection request (whether some sort of credentials must - * be specified, for example). - * - * @param request - * The TunnelRequest associated with the connection request received. - * Any parameters specified along with the connection request can be - * read from this object. - * - * @return - * A newly constructed GuacamoleTunnel if successful, null otherwise. - * - * @throws GuacamoleException - * If an error occurs while constructing the GuacamoleTunnel, or if the - * conditions required for connection are not met. - */ - protected abstract GuacamoleTunnel doConnect(TunnelRequest request) - throws GuacamoleException; - -} - diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java deleted file mode 100644 index df1c008d..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/RestrictedGuacamoleWebSocketTunnelServlet.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file has been modified from the original, upstream version to facilitate - * integration with OpenUDS. - */ - -/* - * 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.tunnel.websocket.tomcat; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.tunnel.TunnelRequest; -import org.apache.guacamole.tunnel.TunnelRequestService; - -/** - * Tunnel servlet implementation which uses WebSocket as a tunnel backend, - * rather than HTTP, properly parsing connection IDs included in the connection - * request. - */ -@Singleton -public class RestrictedGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet { - - /** - * Service for handling tunnel requests. - */ - @Inject - private TunnelRequestService tunnelRequestService; - - @Override - protected GuacamoleTunnel doConnect(TunnelRequest request) - throws GuacamoleException { - return tunnelRequestService.createTunnel(request); - }; - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/WebSocketTunnelModule.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/WebSocketTunnelModule.java deleted file mode 100644 index 34190761..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/WebSocketTunnelModule.java +++ /dev/null @@ -1,70 +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.tunnel.websocket.tomcat; - -import com.google.inject.servlet.ServletModule; -import org.apache.guacamole.tunnel.TunnelLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Loads the Jetty 9 WebSocket tunnel implementation. - * - * @author Michael Jumper - */ -public class WebSocketTunnelModule extends ServletModule implements TunnelLoader { - - /** - * Logger for this class. - */ - private final Logger logger = LoggerFactory.getLogger(WebSocketTunnelModule.class); - - @Override - public boolean isSupported() { - - try { - - // Attempt to find WebSocket servlet - Class.forName("org.apache.guacamole.tunnel.websocket.tomcat.RestrictedGuacamoleWebSocketTunnelServlet"); - - // Support found - return true; - - } - - // If no such servlet class, this particular WebSocket support - // is not present - catch (ClassNotFoundException e) {} - catch (NoClassDefFoundError e) {} - - // Support not found - return false; - - } - - @Override - public void configureServlets() { - - logger.info("Loading Tomcat 7 WebSocket support..."); - serve("/websocket-tunnel").with(RestrictedGuacamoleWebSocketTunnelServlet.class); - - } - -} diff --git a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/package-info.java b/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/package-info.java deleted file mode 100644 index 21002a2c..00000000 --- a/guacamole-tunnel/src/main/java/org/apache/guacamole/tunnel/websocket/tomcat/package-info.java +++ /dev/null @@ -1,25 +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. - */ - -/** - * Tomcat WebSocket tunnel implementation. The classes here require at least - * Tomcat 7.0, and may change significantly as there is no common WebSocket - * API for Java yet. - */ -package org.apache.guacamole.tunnel.websocket.tomcat; diff --git a/guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSServletContextListener.java b/guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSServletContextListener.java deleted file mode 100644 index 4d8f5bf7..00000000 --- a/guacamole-tunnel/src/main/java/org/openuds/guacamole/UDSServletContextListener.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2015 Virtual Cable S.L. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * * Neither the name of Virtual Cable S.L. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package org.openuds.guacamole; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.servlet.GuiceServletContextListener; -import org.apache.guacamole.tunnel.TunnelModule; - -/** - * ServletContextListener implementation which initializes Guice and services - * specific to OpenUDS. - */ -public class UDSServletContextListener extends GuiceServletContextListener { - - @Override - protected Injector getInjector() { - - // Create an injector with OpenUDS- and Guacamole-specific services - // properly bound - return Guice.createInjector( - new UDSModule(), - new TunnelModule() - ); - - } - -} diff --git a/guacamole-tunnel/src/main/webapp/WEB-INF/tunnel.properties b/guacamole-tunnel/src/main/webapp/WEB-INF/tunnel.properties deleted file mode 100644 index 25dca267..00000000 --- a/guacamole-tunnel/src/main/webapp/WEB-INF/tunnel.properties +++ /dev/null @@ -1,5 +0,0 @@ -# We can specify the uds server here as: -# uds=http://172.27.0.1:8000 -# Or we can get the server from an external file. The path part of the URL inside the file will be removed, -# so, if we put "https://example.com/other", the value of uds configuration will be "https://example.com" -udsfile=/etc/uds.conf diff --git a/guacamole-tunnel/src/main/webapp/WEB-INF/web.xml b/guacamole-tunnel/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index f213f720..00000000 --- a/guacamole-tunnel/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - index.xhtml - - - - - org.openuds.guacamole.UDSServletContextListener - - - - - guiceFilter - com.google.inject.servlet.GuiceFilter - - - guiceFilter - /* - - - diff --git a/guacamole-tunnel/src/main/webapp/index.xhtml b/guacamole-tunnel/src/main/webapp/index.xhtml deleted file mode 100644 index 8720f3f7..00000000 --- a/guacamole-tunnel/src/main/webapp/index.xhtml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - UDS Guacamole Access - - - - -
- - -
-
-
-
-
-
- -
- - -
- - -
- - -
- - - - - -
- - -
- - - - - - - - - - - - - - - - - - diff --git a/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty-mobile.xml b/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty-mobile.xml deleted file mode 100644 index 0be8167a..00000000 --- a/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty-mobile.xml +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - - - - - Tab - - - - q - 1 - Q - q - - - - w - 2 - W - w - - - - e - 3 - E - e - - - - r - 4 - R - r - - - - t - 5 - T - t - - - - y - 6 - Y - y - - - - u - 7 - U - u - - - - i - 8 - I - i - - - - o - 9 - O - o - - - - p - 0 - P - p - - - - [ - { - - - - ] - } - - - - Back - - - - - - - - ?123 - - - - a - # - A - a - - - - s - $ - S - s - - - - d - % - D - d - - - - f - & - F - f - - - - g - * - G - g - - - - h - - - H - h - - - - j - + - J - j - - - - k - ( - K - k - - - - l - ) - L - l - - - - ; - : - - - - ' - " - - - - Enter - - - - - - - - Shift - - - - z - < - Z - z - - - - x - > - X - x - - - - c - = - C - c - - - - v - ' - V - v - - - - b - ; - B - b - - - - n - , - N - n - - - - m - . - M - m - - - - , - ! - ! - ! - - - - . - ? - ? - ? - - - - / - ? - - - - Shift - - - - - - - - Ctrl - - - - Super - - - - Alt - - - - - - - - Alt - - - - Menu - - - - Ctrl - - - diff --git a/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty.xml b/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty.xml deleted file mode 100644 index 8a029446..00000000 --- a/guacamole-tunnel/src/main/webapp/layouts/en-us-qwerty.xml +++ /dev/null @@ -1,496 +0,0 @@ - - - - - - - - - - Esc - - - - F1 - - - - F2 - - - - F3 - - - - F4 - - - - F5 - - - - F6 - - - - F7 - - - - F8 - - - - F9 - - - - F10 - - - - F11 - - - - F12 - - - - - - - - - ` - ~ - - - - 1 - ! - - - - 2 - @ - - - - 3 - # - - - - 4 - $ - - - - 5 - % - - - - 6 - ^ - - - - 7 - & - - - - 8 - * - - - - 9 - ( - - - - 0 - ) - - - - - - _ - - - - = - + - - - - Back - - - - - - - - Tab - - - - q - Q - Q - q - - - - w - W - W - w - - - - e - E - E - e - - - - r - R - R - r - - - - t - T - T - t - - - - y - Y - Y - y - - - - u - U - U - u - - - - i - I - I - i - - - - o - O - O - o - - - - p - P - P - p - - - - [ - { - - - - ] - } - - - - \ - | - - - - - - - - Caps - - - - a - A - A - a - - - - s - S - S - s - - - - d - D - D - d - - - - f - F - F - f - - - - g - G - G - g - - - - h - H - H - h - - - - j - J - J - j - - - - k - K - K - k - - - - l - L - L - l - - - - ; - : - - - - ' - " - - - - Enter - - - - - - - - Shift - - - - z - Z - Z - z - - - - x - X - X - x - - - - c - C - C - c - - - - v - V - V - v - - - - b - B - B - b - - - - n - N - N - n - - - - m - M - M - m - - - - , - < - - - - . - > - - - - / - ? - - - - Shift - - - - - - - - Ctrl - - - - Super - - - - Alt - - - - - - - - Alt - - - - Menu - - - - Ctrl - - - - - - - - - - - - Ins - - - - Home - - - - PgUp - - - - - - Del - - - - End - - - - PgDn - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/guacamole-tunnel/src/main/webapp/readme.txt b/guacamole-tunnel/src/main/webapp/readme.txt deleted file mode 100644 index bd3fd491..00000000 --- a/guacamole-tunnel/src/main/webapp/readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -This portion of UDS (HTML5 tunnel) is based on Apache Guacamole. The source -code that produced this war file can be obtained from: - - http://openuds.org/ - -The Apache Guacamole source code is available from: - - http://guacamole.incubator.apache.org/ diff --git a/guacamole-tunnel/src/main/webapp/scripts/client-ui.js b/guacamole-tunnel/src/main/webapp/scripts/client-ui.js deleted file mode 100644 index 7f437e90..00000000 --- a/guacamole-tunnel/src/main/webapp/scripts/client-ui.js +++ /dev/null @@ -1,2370 +0,0 @@ -/* - * Copyright (C) 2017 Glyptodon, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * Client UI root object. - */ -GuacUI.Client = { - - /** - * Enumeration of all tunnel-specific error messages for each applicable - * error code. - */ - "tunnel_errors": { - - 0x0201: "The Guacamole server has rejected this connection attempt \ - because there are too many active connections. Please wait \ - a few minutes and try again.", - - 0x0202: "The connection has been closed because the server is taking \ - too long to respond. This is usually caused by network \ - problems, such as a spotty wireless signal, or slow network \ - speeds. Please check your network connection and try again \ - or contact your system administrator.", - - 0x0203: "The server encountered an error and has closed the \ - connection. Please try again or contact your \ - system administrator.", - - 0x0204: "The requested connection does not exist. Please check the \ - connection name and try again.", - - 0x0205: "This connection is currently in use, and concurrent access to \ - this connection is not allowed. Please try again later.", - - 0x0207: "The Guacamole server is not currently reachable. Please \ - check your network and try again.", - - 0x0208: "The Guacamole server is not accepting connections. Please \ - check your network and try again.", - - 0x0301: "You do not have permission to access this connection because \ - you are not logged in. Please log in and try again.", - - 0x0303: "You do not have permission to access this connection. If you \ - require access, please ask your system administrator to add \ - you the list of allowed users, or check your system settings.", - - 0x0308: "The Guacamole server has closed the connection because there \ - has been no response from your browser for long enough that \ - it appeared to be disconnected. This is commonly caused by \ - network problems, such as spotty wireless signal, or simply \ - very slow network speeds. Please check your network and try \ - again.", - - 0x031D: "The Guacamole server is denying access to this connection \ - because you have exhausted the limit for simultaneous \ - connection use by an individual user. Please close one or \ - more connections and try again.", - - "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." - - }, - - /** - * Enumeration of all client-specific error messages for each applicable - * error code. - */ - "client_errors": { - - 0x0201: "This connection has been closed because the server is busy. \ - Please wait a few minutes and try again.", - - 0x0202: "The Guacamole server has closed the connection because the \ - remote desktop is taking too long to respond. Please try \ - again or contact your system administrator.", - - 0x0203: "The remote desktop server encountered an error and has closed \ - the connection. Please try again or contact your system \ - administrator.", - - 0x0207: "The remote desktop server is currently unreachable. If the \ - problem persists, please notify your system administrator, or \ - check your system logs.", - - 0x0208: "The remote desktop server is currently unavailable. If the \ - problem persists, please notify your system administrator, or \ - check your system logs.", - - 0x0209: "The remote desktop server has closed the connection because \ - it conflicts with another connection. Please try again later.", - - 0x020A: "The remote desktop server has closed the connection because \ - it appeared to be inactive. If this is undesired or \ - unexpected, please notify your system administrator, or check \ - your system settings.", - - 0x020B: "The remote desktop server has forcibly closed the connection. \ - If this is undesired or unexpected, please notify your system \ - administrator, or check your system logs.", - - 0x0301: "Log in failed. Please reconnect and try again.", - - 0x0303: "The remote desktop server has denied access to this \ - connection. If you require access, please ask your system \ - administrator to grant your account access, or check your \ - system settings.", - - 0x0308: "The Guacamole server has closed the connection because there \ - has been no response from your browser for long enough that \ - it appeared to be disconnected. This is commonly caused by \ - network problems, such as spotty wireless signal, or simply \ - very slow network speeds. Please check your network and try \ - again.", - - 0x031D: "The Guacamole server is denying access to this connection \ - because you have exhausted the limit for simultaneous \ - connection use by an individual user. Please close one or \ - more connections and try again.", - - "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." - - }, - - /** - * Enumeration of all error messages for each applicable error code. This - * list is specific to file uploads. - */ - "upload_errors": { - - 0x0100: "File transfer is either not supported or not enabled. Please \ - contact your system administrator, or check your system logs.", - - 0x0201: "Too many files are currently being transferred. Please wait \ - for existing transfers to complete, and then try again.", - - 0x0202: "The file cannot be transferred because the remote desktop \ - server is taking too long to respond. Please try again or \ - or contact your system administrator.", - - 0x0203: "The remote desktop server encountered an error during \ - transfer. Please try again or contact your system \ - administrator.", - - 0x0204: "The destination for the file transfer does not exist. Please \ - check that the destionation exists and try again.", - - 0x0205: "The destination for the file transfer is currently locked. \ - Please wait for any in-progress tasks to complete and try \ - again.", - - 0x0301: "You do not have permission to upload this file because you \ - are not logged in. Please log in and try again.", - - 0x0303: "You do not have permission to upload this file. If you \ - require access, please check your system settings, or \ - check with your system administrator.", - - 0x0308: "The file transfer has stalled. This is commonly caused by \ - network problems, such as spotty wireless signal, or \ - simply very slow network speeds. Please check your \ - network and try again.", - - 0x031D: "Too many files are currently being transferred. Please wait \ - for existing transfers to complete, and then try again.", - - "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.", - - }, - - /** - * All error codes for which automatic reconnection is appropriate when a - * tunnel error occurs. - */ - "tunnel_auto_reconnect": { - 0x0200: true, - 0x0202: true, - 0x0203: true, - 0x0207: true, - 0x0208: true, - 0x0308: true - }, - - /** - * All error codes for which automatic reconnection is appropriate when a - * client error occurs. - */ - "client_auto_reconnect": { - 0x0200: true, - 0x0202: true, - 0x0203: true, - 0x0207: true, - 0x0208: true, - 0x0301: true, - 0x0308: true - }, - - /* Constants */ - - "KEYBOARD_AUTO_RESIZE_INTERVAL" : 30, /* milliseconds */ - "RECONNECT_PERIOD" : 15, /* seconds */ - "TEXT_INPUT_PADDING" : 128, /* characters */ - "TEXT_INPUT_PADDING_CODEPOINT" : 0x200B, - - /* Main application area */ - - "viewport" : document.getElementById("viewportClone"), - "main" : document.getElementById("main"), - "display" : document.getElementById("display"), - "notification_area" : document.getElementById("notificationArea"), - - /* Text input */ - - "text_input" : { - "container" : document.getElementById("text-input"), - "sent" : document.getElementById("sent-history"), - "target" : document.getElementById("target"), - "enabled" : false - }, - - /* Menu */ - - "menu" : document.getElementById("menu"), - "menu_title" : document.getElementById("menu-title"), - "clipboard" : document.getElementById("clipboard"), - "relative_radio" : document.getElementById("relative"), - "absolute_radio" : document.getElementById("absolute"), - "ime_none_radio" : document.getElementById("ime-none"), - "ime_text_radio" : document.getElementById("ime-text"), - "ime_osk_radio" : document.getElementById("ime-osk"), - "zoom_state" : document.getElementById("zoom-state"), - "zoom_out" : document.getElementById("zoom-out"), - "zoom_in" : document.getElementById("zoom-in"), - "auto_fit" : document.getElementById("auto-fit"), - - "min_zoom" : 1, - "max_zoom" : 3, - - "connectionName" : "UDS Remote Connection", - "attachedClient" : null, - - /* Mouse emulation */ - - "emulate_absolute" : true, - "touch" : null, - "touch_screen" : null, - "touch_pad" : null, - - /* Clipboard */ - - "remote_clipboard" : "", - "clipboard_integration_enabled" : undefined - -}; - -/** - * On-screen Keyboard. This component provides a clickable/touchable keyboard - * which sends key events to the Guacamole client. - * - * @constructor - */ -GuacUI.Client.OnScreenKeyboard = new (function() { - - /** - * Event target. This is a hidden textarea element which will receive - * key events. - * @private - */ - var keyboard_container = GuacUI.createElement("div", "keyboard-container"); - - var keyboard_resize_interval = null; - - // On-screen keyboard - var keyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml"); - keyboard_container.appendChild(keyboard.getElement()); - - var last_keyboard_width = 0; - - // Function for automatically updating keyboard size - function updateKeyboardSize() { - var currentSize = keyboard.getElement().offsetWidth; - if (last_keyboard_width != currentSize) { - keyboard.resize(currentSize); - last_keyboard_width = currentSize; - } - } - - keyboard.onkeydown = function(keysym) { - if (GuacUI.Client.attachedClient) - GuacUI.Client.attachedClient.sendKeyEvent(1, keysym); - }; - - keyboard.onkeyup = function(keysym) { - if (GuacUI.Client.attachedClient) - GuacUI.Client.attachedClient.sendKeyEvent(0, keysym); - }; - - this.show = function() { - - // Only add if not already present - if (keyboard_container.parentNode === document.body) - return; - - // Show keyboard - document.body.appendChild(keyboard_container); - - // Start periodic update of keyboard size - keyboard_resize_interval = window.setInterval( - updateKeyboardSize, - GuacUI.Client.KEYBOARD_AUTO_RESIZE_INTERVAL); - - // Resize on window resize - window.addEventListener("resize", updateKeyboardSize, true); - - // Initialize size - updateKeyboardSize(); - - }; - - this.hide = function() { - - // Only remove if present - if (keyboard_container.parentNode !== document.body) - return; - - // Hide keyboard - document.body.removeChild(keyboard_container); - window.clearInterval(keyboard_resize_interval); - window.removeEventListener("resize", updateKeyboardSize, true); - - }; - -})(); - -/** - * Modal status display. Displays a message to the user, covering the entire - * screen. - * - * Normally, this should only be used when user interaction with other - * components is impossible. - * - * @constructor - */ -GuacUI.Client.ModalStatus = function(title_text, text, classname, reconnect) { - - // Create element hierarchy - var outer = GuacUI.createElement("div", "dialogOuter"); - var middle = GuacUI.createChildElement(outer, "div", "dialogMiddle"); - var dialog = GuacUI.createChildElement(middle, "div", "dialog"); - - // Add title if given - if (title_text) { - var title = GuacUI.createChildElement(dialog, "p", "title"); - title.textContent = title_text; - } - - var status = GuacUI.createChildElement(dialog, "p", "status"); - status.textContent = text; - - // Set classname if given - if (classname) - GuacUI.addClass(outer, classname); - - // Automatically reconnect after the given time period - var reconnect_interval = null; - var reconnect_forced = false; - - /** - * Stops the reconnect countdown and forces a client reconnect. - */ - function force_reconnect() { - if (!reconnect_forced) { - reconnect_forced = true; - window.clearInterval(reconnect_interval); - GuacUI.Client.connect(); - } - } - - if (reconnect) { - - var countdown = GuacUI.createChildElement(dialog, "p", "countdown"); - - function update_status() { - - // Use appropriate description of time remaining - if (reconnect === 0) - countdown.textContent = "Reconnecting..."; - if (reconnect === 1) - countdown.textContent = "Reconnecting in 1 second..."; - else - countdown.textContent = "Reconnecting in " + reconnect + " seconds..."; - - // Reconnect if countdown complete - if (reconnect === 0) - force_reconnect(); - - } - - // Update counter every second - reconnect_interval = window.setInterval(function update_countdown() { - reconnect--; - update_status(); - }, 1000); - - // Init status - update_status(); - - } - - // Reconnect button - var reconnect_section = GuacUI.createChildElement(dialog, "div", "reconnect"); - var reconnect_button = GuacUI.createChildElement(reconnect_section, "button"); - reconnect_button.textContent = "Reconnect"; - - // Reconnect if button clicked - reconnect_button.onclick = force_reconnect; - - // Reconnect if button tapped - reconnect_button.addEventListener("touchend", function(e) { - if (e.touches.length === 0) - force_reconnect(); - }, true); - - this.show = function() { - document.body.appendChild(outer); - }; - - this.hide = function() { - window.clearInterval(reconnect_interval); - document.body.removeChild(outer); - }; - -}; - -/** - * Monitors a given element for touch events, firing drag-specific events - * based on pre-defined gestures. - * - * @constructor - * @param {Element} element The element to monitor for touch events. - */ -GuacUI.Client.Drag = function(element) { - - /** - * Reference to this drag instance. - * @private - */ - var guac_drag = this; - - /** - * Whether a drag gestures is in progress. - */ - var in_progress = false; - - /** - * The starting X location of the drag gesture. - */ - this.start_x = null; - - /** - * The starting Y location of the drag gesture. - */ - this.start_y = null; - - /** - * The change in X relative to drag start. - */ - this.delta_x = 0; - - /** - * The change in X relative to drag start. - */ - this.delta_y = 0; - - /** - * Called when a drag gesture begins. - * - * @event - * @param {Number} x The relative change in X location relative to - * drag start. For drag start, this will ALWAYS be 0. - * @param {Number} y The relative change in Y location relative to - * drag start. For drag start, this will ALWAYS be 0. - */ - this.ondragstart = null; - - /** - * Called when the drag amount changes. - * - * @event - * @param {Number} x The relative change in X location relative to - * drag start. - * @param {Number} y The relative change in Y location relative to - * drag start. - */ - this.ondragchange = null; - - /** - * Called when a drag gesture ends. - * - * @event - * @param {Number} x The relative change in X location relative to - * drag start. - * @param {Number} y The relative change in Y location relative to - * drag start. - */ - this.ondragend = null; - - /** - * Cancels the current drag gesture, if any. Drag events will cease to fire - * until a new gesture begins. - */ - this.cancel = function() { - in_progress = false; - }; - - // When there is exactly one touch, monitor the change in location - element.addEventListener("touchmove", function(e) { - if (e.touches.length === 1) { - - e.preventDefault(); - e.stopPropagation(); - - // Get touch location - var x = e.touches[0].clientX; - var y = e.touches[0].clientY; - - // If gesture just starting, fire zoom start - if (!guac_drag.start_x || !guac_drag.start_y) { - guac_drag.start_x = x; - guac_drag.start_y = y; - guac_drag.delta_x = 0; - guac_drag.delta_y = 0; - in_progress = true; - if (guac_drag.ondragstart) - guac_drag.ondragstart(guac_drag.delta_x, guac_drag.delta_y); - } - - // Otherwise, notify of zoom change - else if (guac_drag.ondragchange) { - guac_drag.delta_x = x - guac_drag.start_x; - guac_drag.delta_y = y - guac_drag.start_y; - - if (in_progress) - guac_drag.ondragchange(guac_drag.delta_x, guac_drag.delta_y); - } - - } - }, false); - - // Reset monitoring and fire end event when done - element.addEventListener("touchend", function(e) { - - if (guac_drag.start_x && guac_drag.start_y && e.touches.length === 0) { - - e.preventDefault(); - e.stopPropagation(); - - if (in_progress && guac_drag.ondragend) - guac_drag.ondragend(); - - guac_drag.start_x = null; - guac_drag.start_y = null; - guac_drag.delta_x = 0; - guac_drag.delta_y = 0; - in_progress = false; - - } - - }, false); - -}; - -/** - * Monitors a given element for touch events, firing zoom-specific events - * based on pre-defined gestures. - * - * @constructor - * @param {Element} element The element to monitor for touch events. - */ -GuacUI.Client.Pinch = function(element) { - - /** - * Reference to this zoom instance. - * @private - */ - var guac_zoom = this; - - /** - * The current pinch distance, or null if the gesture has not yet started. - * @private - */ - var start_length = null; - - /** - * The current zoom ratio. - * @type Number - */ - this.ratio = 1; - - /** - * The X-coordinate of the current center of the pinch gesture. - * @type Number - */ - this.centerX = 0; - - /** - * The Y-coordinate of the current center of the pinch gesture. - * @type Number - */ - this.centerY = 0; - - /** - * Called when a zoom gesture begins. - * - * @event - * @param {Number} ratio The relative value of the starting zoom. This will - * ALWAYS be 1. - * @param {Number} x The X-coordinate of the center of the pinch gesture. - * @param {Number} y The Y-coordinate of the center of the pinch gesture. - */ - this.onzoomstart = null; - - /** - * Called when the amount of zoom changes. - * - * @event - * @param {Number} ratio The relative value of the changed zoom, with 1 - * being no change. - * @param {Number} x The X-coordinate of the center of the pinch gesture. - * @param {Number} y The Y-coordinate of the center of the pinch gesture. - */ - this.onzoomchange = null; - - /** - * Called when a zoom gesture ends. - * - * @event - * @param {Number} ratio The relative value of the final zoom, with 1 - * being no change. - * @param {Number} x The X-coordinate of the center of the pinch gesture. - * @param {Number} y The Y-coordinate of the center of the pinch gesture. - */ - this.onzoomend = null; - - /** - * Given a touch event, calculates the distance between the first two - * touches in pixels. - * - * @param {TouchEvent} e The touch event to use when performing distance - * calculation. - * @return {Number} The distance in pixels between the first two touches. - */ - function pinch_distance(e) { - - var touch_a = e.touches[0]; - var touch_b = e.touches[1]; - - var delta_x = touch_a.clientX - touch_b.clientX; - var delta_y = touch_a.clientY - touch_b.clientY; - - return Math.sqrt(delta_x*delta_x + delta_y*delta_y); - - } - - /** - * Given a touch event, calculates the center between the first two - * touches in pixels, returning the X coordinate of this center. - * - * @param {TouchEvent} e The touch event to use when performing center - * calculation. - * @return {Number} The X-coordinate of the center of the first two touches. - */ - function pinch_center_x(e) { - - var touch_a = e.touches[0]; - var touch_b = e.touches[1]; - - return (touch_a.clientX + touch_b.clientX) / 2; - - } - - /** - * Given a touch event, calculates the center between the first two - * touches in pixels, returning the Y coordinate of this center. - * - * @param {TouchEvent} e The touch event to use when performing center - * calculation. - * @return {Number} The Y-coordinate of the center of the first two touches. - */ - function pinch_center_y(e) { - - var touch_a = e.touches[0]; - var touch_b = e.touches[1]; - - return (touch_a.clientY + touch_b.clientY) / 2; - - } - - // When there are exactly two touches, monitor the distance between - // them, firing zoom events as appropriate - element.addEventListener("touchmove", function(e) { - if (e.touches.length === 2) { - - e.preventDefault(); - e.stopPropagation(); - - // Calculate current zoom level - var current = pinch_distance(e); - - // Calculate center - guac_zoom.centerX = pinch_center_x(e); - guac_zoom.centerY = pinch_center_y(e); - - // If gesture just starting, fire zoom start - if (!start_length) { - start_length = current; - guac_zoom.ratio = 1; - if (guac_zoom.onzoomstart) - guac_zoom.onzoomstart(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); - } - - // Otherwise, notify of zoom change - else { - guac_zoom.ratio = current / start_length; - if (guac_zoom.onzoomchange) - guac_zoom.onzoomchange(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); - } - - } - }, false); - - // Reset monitoring and fire end event when done - element.addEventListener("touchend", function(e) { - - if (start_length && e.touches.length < 2) { - - e.preventDefault(); - e.stopPropagation(); - - start_length = null; - if (guac_zoom.onzoomend) - guac_zoom.onzoomend(guac_zoom.ratio, guac_zoom.centerX, guac_zoom.centerY); - guac_zoom.ratio = 1; - } - - }, false); - -}; - -/** - * Sets the current display scale to the given value, where 1 is 100% (1:1 - * pixel ratio). Out-of-range values will be clamped in-range. - * - * @param {Number} new_scale The new scale to apply - */ -GuacUI.Client.setScale = function(new_scale) { - - new_scale = Math.max(new_scale, GuacUI.Client.min_zoom); - new_scale = Math.min(new_scale, GuacUI.Client.max_zoom); - - if (GuacUI.Client.attachedClient) - GuacUI.Client.attachedClient.getDisplay().scale(new_scale); - - GuacUI.Client.zoom_state.textContent = Math.round(new_scale * 100) + "%"; - - // If at minimum zoom level, auto fit is ON - if (new_scale === GuacUI.Client.min_zoom) { - GuacUI.Client.main.style.overflow = "hidden"; - GuacUI.Client.auto_fit.checked = true; - GuacUI.Client.auto_fit.disabled = (GuacUI.Client.min_zoom >= 1); - } - - // If at minimum zoom level, auto fit is OFF - else { - GuacUI.Client.main.style.overflow = "auto"; - GuacUI.Client.auto_fit.checked = false; - GuacUI.Client.auto_fit.disabled = false; - } - -}; - -/** - * Updates the scale of the attached Guacamole.Client based on current window - * size and "auto-fit" setting. - */ -GuacUI.Client.updateDisplayScale = function() { - - var guac = GuacUI.Client.attachedClient; - if (!guac) - return; - - // Determine whether display is currently fit to the screen - var auto_fit = (guac.getDisplay().getScale() === GuacUI.Client.min_zoom); - - // Calculate scale to fit screen - GuacUI.Client.min_zoom = Math.min( - GuacUI.Client.main.offsetWidth / Math.max(guac.getDisplay().getWidth(), 1), - GuacUI.Client.main.offsetHeight / Math.max(guac.getDisplay().getHeight(), 1) - ); - - // Calculate appropriate maximum zoom level - GuacUI.Client.max_zoom = Math.max(GuacUI.Client.min_zoom, 3); - - // Clamp zoom level, maintain auto-fit - if (guac.getDisplay().getScale() < GuacUI.Client.min_zoom || auto_fit) - GuacUI.Client.setScale(GuacUI.Client.min_zoom); - - else if (guac.getDisplay().getScale() > GuacUI.Client.max_zoom) - GuacUI.Client.setScale(GuacUI.Client.max_zoom); - -}; - -/** - * Updates the document title based on the connection name. - */ -GuacUI.Client.updateTitle = function () { - - if (GuacUI.Client.titlePrefix) - document.title = GuacUI.Client.titlePrefix + " " + GuacUI.Client.connectionName; - else - document.title = GuacUI.Client.connectionName; - - GuacUI.Client.menu_title.textContent = GuacUI.Client.connectionName; - -}; - -/** - * Sets whether the menu is currently visible. Keyboard is disabled while the - * menu is shown. - * - * @param {Boolean} [shown] Whether the menu should be shown. If omitted, this - * function will cause the menu to be shown by default. - */ -GuacUI.Client.showMenu = function(shown) { - if (shown === false) { - GuacUI.Client.menu.className = "closed"; - GuacUI.Client.commitClipboard(); - } - else - GuacUI.Client.menu.className = "open"; -}; - -/** - * Sets whether the text input box is currently visible. - * - * @param {Boolean} [shown] Whether the text input box should be shown. If - * omitted, this function will cause the menu to be - * shown by default. - */ -GuacUI.Client.showTextInput = function(shown) { - if (shown === false) { - GuacUI.Client.text_input.container.className = "closed"; - GuacUI.Client.text_input.target.blur(); - } - else { - GuacUI.Client.text_input.container.className = "open"; - GuacUI.Client.text_input.target.focus(); - } -}; - -/** - * Returns whether the menu is currently shown. - * - * @returns {Boolean} true if the menu is shown, false otherwise. - */ -GuacUI.Client.isMenuShown = function() { - return GuacUI.Client.menu.className === "open"; -}; - -/** - * Hides the currently-visible status overlay, if any. - */ -GuacUI.Client.hideStatus = function() { - if (GuacUI.Client.visibleStatus) - GuacUI.Client.visibleStatus.hide(); - GuacUI.Client.visibleStatus = null; -}; - -/** - * Displays a status overlay with the given text. - */ -GuacUI.Client.showStatus = function(title, status) { - GuacUI.Client.hideStatus(); - - GuacUI.Client.visibleStatus = new GuacUI.Client.ModalStatus(title, status); - GuacUI.Client.visibleStatus.show(); -}; - -/** - * Displays an error status overlay with the given text. - */ -GuacUI.Client.showError = function(title, status, reconnect) { - GuacUI.Client.hideStatus(); - - GuacUI.Client.visibleStatus = - new GuacUI.Client.ModalStatus(title, status, "guac-error", reconnect); - GuacUI.Client.visibleStatus.show(); -}; - -GuacUI.Client.showNotification = function(message) { - - // Create notification - var element = GuacUI.createElement("div", "message notification"); - GuacUI.createChildElement(element, "div", "caption").textContent = message; - - // Add to DOM - GuacUI.Client.notification_area.appendChild(element); - - // Remove from DOM after around 5 seconds - window.setTimeout(function() { - GuacUI.Client.notification_area.removeChild(element); - }, 5000); - -}; - -/** - * Connects to the current Guacamole connection, attaching a new Guacamole - * client to the user interface. If a Guacamole client is already attached, - * it is replaced. - */ -GuacUI.Client.connect = function() { - - var tunnel; - - // If WebSocket available, try to use it. - if (window.WebSocket) - tunnel = new Guacamole.ChainedTunnel( - new Guacamole.WebSocketTunnel("websocket-tunnel"), - new Guacamole.HTTPTunnel("tunnel") - ); - - // If no WebSocket, then use HTTP. - else - tunnel = new Guacamole.HTTPTunnel("tunnel"); - - // Instantiate client - var guac = new Guacamole.Client(tunnel); - - // Tie UI to client - GuacUI.Client.attach(guac); - - // Calculate optimal width/height for display - var pixel_density = window.devicePixelRatio || 1; - var optimal_dpi = pixel_density * 96; - var optimal_width = window.innerWidth * pixel_density; - var optimal_height = window.innerHeight * pixel_density; - - // Scale width/height to be at least 600x600 - if (optimal_width < 600 || optimal_height < 600) { - var scale = Math.max(600 / optimal_width, 600 / optimal_height); - optimal_width = optimal_width * scale; - optimal_height = optimal_height * scale; - } - - // Get entire query string, and pass to connect(). - // Normally, only the "id" parameter is required, but - // all parameters should be preserved and passed on for - // the sake of authentication. - - var queryArr = []; - for( k in window.query ) { - queryArr.push(k+"="+window.query[k]) - } - - var connect_string = - queryArr.join('&') - + "&GUAC_WIDTH=" + Math.floor(optimal_width) - + "&GUAC_HEIGHT=" + Math.floor(optimal_height) - + "&GUAC_DPI=" + Math.floor(optimal_dpi); - - // Add audio mimetypes to connect_string - GuacUI.Audio.supported.forEach(function(mimetype) { - connect_string += "&GUAC_AUDIO=" + encodeURIComponent(mimetype); - }); - - // Add video mimetypes to connect_string - GuacUI.Video.supported.forEach(function(mimetype) { - connect_string += "&GUAC_VIDEO=" + encodeURIComponent(mimetype); - }); - - // Show connection errors from tunnel - tunnel.onerror = function(status) { - var message = GuacUI.Client.tunnel_errors[status.code] || GuacUI.Client.tunnel_errors.DEFAULT; - GuacUI.Client.showError("Connection Error", message, - GuacUI.Client.tunnel_auto_reconnect[status.code] && GuacUI.Client.RECONNECT_PERIOD); - }; - - // Notify of disconnections (if not already notified of something else) - tunnel.onstatechange = function(state) { - if (state === Guacamole.Tunnel.State.CLOSED && !GuacUI.Client.visibleStatus) - // window.location = window.query.exit; - // Better do this, disabling possible xss - window.close() - }; - - // Connect - guac.connect(connect_string); - - -}; - -/** - * Represents a number of bytes as a human-readable size string, including - * units. - * - * @param {Number} bytes The number of bytes. - * @returns {String} A human-readable string containing the size given. - */ -GuacUI.Client.getSizeString = function(bytes) { - - if (bytes > 1000000000) - return (bytes / 1000000000).toFixed(1) + " GB"; - - else if (bytes > 1000000) - return (bytes / 1000000).toFixed(1) + " MB"; - - else if (bytes > 1000) - return (bytes / 1000).toFixed(1) + " KB"; - - else - return bytes + " B"; - -}; - -/** - * Commits the current contents of the clipboard textarea to session storage, - * and thus to the remote clipboard if the client is connected. - */ -GuacUI.Client.commitClipboard = function() { - var new_value = GuacUI.Client.clipboard.value; - GuacamoleSessionStorage.setItem("clipboard", new_value); -}; - -/** - * Sets the contents of the remote clipboard, if the contents given are - * different. - * - * @param {String} data The data to assign to the clipboard. - */ -GuacUI.Client.setClipboard = function(data) { - - if (data !== GuacUI.Client.remote_clipboard && GuacUI.Client.attachedClient) { - GuacUI.Client.remote_clipboard = data; - //GuacUI.Client.attachedClient.setClipboard(data); - // Old setClipboard funtion - var stream = GuacUI.Client.attachedClient.createClipboardStream('text/plain') - var writer = new Guacamole.StringWriter(stream); - - // Send text chunks - for (var i=0; i view_width) - scroll_amount_x = mouse_view_x - view_width; - else if (mouse_view_x < 0) - scroll_amount_x = mouse_view_x; - else - scroll_amount_x = 0; - - var scroll_amount_y; - if (mouse_view_y > view_height) - scroll_amount_y = mouse_view_y - view_height; - else if (mouse_view_y < 0) - scroll_amount_y = mouse_view_y; - else - scroll_amount_y = 0; - - // Scroll (if necessary) to keep mouse on screen. - GuacUI.Client.main.scrollLeft += scroll_amount_x; - GuacUI.Client.main.scrollTop += scroll_amount_y; - - // Scale event by current scale - var scaledState = new Guacamole.Mouse.State( - mouseState.x / guac.getDisplay().getScale(), - mouseState.y / guac.getDisplay().getScale(), - mouseState.left, - mouseState.middle, - mouseState.right, - mouseState.up, - mouseState.down); - - // Send mouse event - guac.sendMouseState(scaledState); - - }; - - var new_mode, old_mode; - GuacUI.Client.emulate_absolute = absolute; - - // Switch to touchscreen if absolute - if (absolute) { - new_mode = GuacUI.Client.touch_screen; - old_mode = GuacUI.Client.touch; - } - - // Switch to touchpad if not absolute (relative) - else { - new_mode = GuacUI.Client.touch_pad; - old_mode = GuacUI.Client.touch; - } - - // Perform switch - if (new_mode) { - - if (old_mode) { - old_mode.onmousedown = old_mode.onmouseup = old_mode.onmousemove = null; - new_mode.currentState.x = old_mode.currentState.x; - new_mode.currentState.y = old_mode.currentState.y; - } - - new_mode.onmousedown = new_mode.onmouseup = new_mode.onmousemove = __handle_mouse_state; - GuacUI.Client.touch = new_mode; - } - -}; - -/** - * Attaches a Guacamole.Client to the client UI, such that Guacamole events - * affect the UI, and local events affect the Guacamole.Client. If a client - * is already attached, it is replaced. - * - * @param {Guacamole.Client} guac The Guacamole.Client to attach to the UI. - */ -GuacUI.Client.attach = function(guac) { - - // If a client is already attached, ensure it is disconnected - if (GuacUI.Client.attachedClient) - GuacUI.Client.attachedClient.disconnect(); - - // Store attached client - GuacUI.Client.attachedClient = guac; - - // Get display element - var guac_display = guac.getDisplay().getElement(); - - /* - * Update the scale of the display when the client display size changes. - */ - - guac.getDisplay().onresize = function(width, height) { - GuacUI.Client.updateDisplayScale(); - }; - - /* - * Update UI when the state of the Guacamole.Client changes. - */ - - guac.onstatechange = function(clientState) { - - switch (clientState) { - - // Idle - case 0: - GuacUI.Client.showStatus(null, "Idle."); - GuacUI.Client.titlePrefix = "[Idle]"; - break; - - // Connecting - case 1: - GuacUI.Client.showStatus("Connecting", "Connecting to Guacamole..."); - GuacUI.Client.titlePrefix = "[Connecting...]"; - break; - - // Connected + waiting - case 2: - GuacUI.Client.showStatus("Connecting", "Connected to Guacamole. Waiting for response..."); - GuacUI.Client.titlePrefix = "[Waiting...]"; - break; - - // Connected - case 3: - - GuacUI.Client.hideStatus(); - GuacUI.Client.titlePrefix = null; - - // Update clipboard with current data - var clipboard = GuacamoleSessionStorage.getItem("clipboard"); - if (clipboard) - GuacUI.Client.setClipboard(clipboard); - - break; - - // Disconnecting / disconnected are handled by tunnel instead - case 4: - case 5: - break; - - // Unknown status code - default: - GuacUI.Client.showStatus("Unknown Status", "An unknown status code was received. This is most likely a bug."); - - } - - GuacUI.Client.updateTitle(); - - }; - - /* - * Change UI to reflect the connection name - */ - - guac.onname = function(name) { - GuacUI.Client.connectionName = name; - GuacUI.Client.updateTitle(); - }; - - /* - * Disconnect and display an error message when the Guacamole.Client - * receives an error. - */ - - guac.onerror = function(status) { - - // Disconnect, if connected - guac.disconnect(); - - // Display error message - var message = GuacUI.Client.client_errors[status.code] || GuacUI.Client.client_errors.DEFAULT; - GuacUI.Client.showError("Connection Error", message, - GuacUI.Client.client_auto_reconnect[status.code] && GuacUI.Client.RECONNECT_PERIOD); - - }; - - // Server copy handler - guac.onclipboard = function(stream, mimetype) { - - // Only text/plain is supported for now - if (mimetype !== "text/plain") { - stream.sendAck("Only text/plain supported", Guacamole.Status.Code.UNSUPPORTED); - return; - } - - var reader = new Guacamole.StringReader(stream); - var data = ""; - - // Append any received data to buffer - reader.ontext = function clipboard_text_received(text) { - data += text; - stream.sendAck("Received", Guacamole.Status.Code.SUCCESS); - }; - - // Set contents when done - reader.onend = function clipboard_text_end() { - GuacUI.Client.remote_clipboard = data; - GuacamoleSessionStorage.setItem("clipboard", data); - }; - - }; - - /* - * Prompt to download file when file received. - */ - - guac.onfile = function(stream, mimetype, filename) { - - var download = new GuacUI.Download(filename); - download.updateProgress(GuacUI.Client.getSizeString(0)); - - var blob_reader = new Guacamole.BlobReader(stream, mimetype); - - GuacUI.Client.notification_area.appendChild(download.getElement()); - - // Update progress as data is received - blob_reader.onprogress = function() { - download.updateProgress(GuacUI.Client.getSizeString(blob_reader.getLength())); - stream.sendAck("Received", 0x0000); - }; - - // When complete, prompt for download - blob_reader.onend = function() { - - download.ondownload = function() { - saveAs(blob_reader.getBlob(), filename); - }; - - download.complete(); - - }; - - // When close clicked, remove from notification area - download.onclose = function() { - GuacUI.Client.notification_area.removeChild(download.getElement()); - }; - - stream.sendAck("Ready", 0x0000); - - }; - - /* - * Do nothing when the display element is clicked on. - */ - - guac_display.onclick = function(e) { - e.preventDefault(); - return false; - }; - - /* - * Handle mouse and touch events relative to the display element. - */ - - // Touchscreen - var touch_screen = new Guacamole.Mouse.Touchscreen(guac_display); - GuacUI.Client.touch_screen = touch_screen; - - // Touchpad - var touch_pad = new Guacamole.Mouse.Touchpad(guac_display); - GuacUI.Client.touch_pad = touch_pad; - - // Init emulation mode for client - GuacUI.Client.setMouseEmulationAbsolute(GuacUI.Client.absolute_radio.checked); - - // Mouse - var mouse = new Guacamole.Mouse(guac_display); - mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = function(mouseState) { - - // Scale event by current scale - var scaledState = new Guacamole.Mouse.State( - mouseState.x / guac.getDisplay().getScale(), - mouseState.y / guac.getDisplay().getScale(), - mouseState.left, - mouseState.middle, - mouseState.right, - mouseState.up, - mouseState.down); - - // Send mouse event - guac.sendMouseState(scaledState); - - }; - - - // Hide any existing status notifications - GuacUI.Client.hideStatus(); - - // Remove old client from UI, if any - GuacUI.Client.display.innerHTML = ""; - - // Add client to UI - guac.getDisplay().getElement().className = "software-cursor"; - GuacUI.Client.display.appendChild(guac.getDisplay().getElement()); - -}; - -// One-time UI initialization -(function() { - - var i; - - /** - * Keys which should be allowed through to the client when in text input - * mode, providing corresponding key events are received. Keys in this - * set will be allowed through to the server. - */ - var IME_ALLOWED_KEYS = { - 0xFF08: true, /* Backspace */ - 0xFF09: true, /* Tab */ - 0xFF0D: true, /* Enter */ - 0xFF1B: true, /* Escape */ - 0xFF50: true, /* Home */ - 0xFF51: true, /* Left */ - 0xFF52: true, /* Up */ - 0xFF53: true, /* Right */ - 0xFF54: true, /* Down */ - 0xFF57: true, /* End */ - 0xFF64: true, /* Insert */ - 0xFFBE: true, /* F1 */ - 0xFFBF: true, /* F2 */ - 0xFFC0: true, /* F3 */ - 0xFFC1: true, /* F4 */ - 0xFFC2: true, /* F5 */ - 0xFFC3: true, /* F6 */ - 0xFFC4: true, /* F7 */ - 0xFFC5: true, /* F8 */ - 0xFFC6: true, /* F9 */ - 0xFFC7: true, /* F10 */ - 0xFFC8: true, /* F11 */ - 0xFFC9: true, /* F12 */ - 0xFFE1: true, /* Left shift */ - 0xFFE2: true, /* Right shift */ - 0xFFFF: true /* Delete */ - }; - - /* - * Route document-level keyboard events to the client. - */ - - var sink = new Guacamole.InputSink(); - document.body.appendChild(sink.getElement()); - - var keyboard = new Guacamole.Keyboard(document); - keyboard.listenTo(sink.getElement()); - var show_keyboard_gesture_possible = true; - - function __send_key(pressed, keysym) { - - // Do not send key if menu shown - if (GuacUI.Client.isMenuShown()) - return true; - - // Allow all but specific keys through to browser when in IME mode - if (GuacUI.Client.text_input.enabled && !IME_ALLOWED_KEYS[keysym]) - return true; - - GuacUI.Client.attachedClient.sendKeyEvent(pressed, keysym); - return false; - - } - - keyboard.onkeydown = function (keysym) { - - // Only handle key events if client is attached - var guac = GuacUI.Client.attachedClient; - if (!guac) return true; - - // Handle Ctrl-shortcuts specifically - if (keyboard.modifiers.ctrl && !keyboard.modifiers.alt && !keyboard.modifiers.shift) { - - // Allow event through if Ctrl+C or Ctrl+X - if (keyboard.pressed[0x63] || keyboard.pressed[0x78]) { - __send_key(1, keysym); - return true; - } - - // If Ctrl+V, wait until after paste event (next event loop) - if (keyboard.pressed[0x76]) { - window.setTimeout(function after_paste() { - __send_key(1, keysym); - }, 10); - return true; - } - - } - - // If key is NOT one of the expected keys, gesture not possible - if (keysym !== 0xFFE3 && keysym !== 0xFFE9 && keysym !== 0xFFE1) - show_keyboard_gesture_possible = false; - - // Send key event - return __send_key(1, keysym); - - }; - - keyboard.onkeyup = function (keysym) { - - // Only handle key events if client is attached - var guac = GuacUI.Client.attachedClient; - if (!guac) return true; - - // If lifting up on shift, toggle menu visibility if rest of gesture - // conditions satisfied - if (show_keyboard_gesture_possible && keysym === 0xFFE1 - && keyboard.pressed[0xFFE3] && keyboard.pressed[0xFFE9]) { - __send_key(0, 0xFFE1); - __send_key(0, 0xFFE9); - __send_key(0, 0xFFE3); - GuacUI.Client.showMenu(!GuacUI.Client.isMenuShown()); - } - - // Detect if no keys are pressed - var reset_gesture = true; - for (var pressed in keyboard.pressed) { - reset_gesture = false; - break; - } - - // Reset gesture state if possible - if (reset_gesture) - show_keyboard_gesture_possible = true; - - // Send key event - return __send_key(0, keysym); - - }; - - /** - * Returns the contents of the remote clipboard if clipboard integration is - * enabled, and null otherwise. - */ - function get_clipboard_data() { - - // If integration not enabled, do not attempt retrieval - if (GuacUI.Client.clipboard_integration_enabled === false) - return null; - - // Otherwise, attempt retrieval and update integration status - try { - var data = GuacamoleService.Clipboard.get(); - GuacUI.Client.clipboard_integration_enabled = true; - return data; - } - catch (status) { - GuacUI.Client.clipboard_integration_enabled = false; - return null; - } - - } - - // Set local clipboard contents on cut - document.body.addEventListener("cut", function handle_cut(e) { - var data = get_clipboard_data(); - if (data !== null) { - e.preventDefault(); - e.clipboardData.setData("text/plain", data); - } - }, false); - - // Set local clipboard contents on copy - document.body.addEventListener("copy", function handle_copy(e) { - var data = get_clipboard_data(); - if (data !== null) { - e.preventDefault(); - e.clipboardData.setData("text/plain", data); - } - }, false); - - // Set remote clipboard contents on paste - document.body.addEventListener("paste", function handle_paste(e) { - - // If status of clipboard integration is unknown, attempt to define it - if (GuacUI.Client.clipboard_integration_enabled === undefined) - get_clipboard_data(); - - // Override and handle paste only if integration is enabled - if (GuacUI.Client.clipboard_integration_enabled) { - e.preventDefault(); - GuacUI.Client.setClipboard(e.clipboardData.getData("text/plain")); - } - - }, false); - - /* - * Disconnect on close - */ - window.onunload = function() { - if (GuacUI.Client.attachedClient) - GuacUI.Client.attachedClient.disconnect(); - }; - - /** - * Calculates maximum possible height that can be scrolled. This helps avoid - * the constant increasing value of scrollHeight on IOS Safari. The - * continuous increase of height causes a crash on IOS Safari when the - * unnaturally high value of scrollHeight is passed to the scrollTo - * function. Therefore in the case where the scrollHeight parameter exceeds - * the maximum possible height value, the return value of this parameter is - * used instead. - * - * For additional info, visit: - * https://muffinman.io/ios-safari-get-bounding-client-rect-bug/ - * - * @returns {number} A number that represents the max height in pixels. - */ - function getPageMaxHeight() { - return Math.max( - document.body.scrollHeight, - document.body.offsetHeight, - document.documentElement.clientHeight, - document.documentElement.scrollHeight, - document.documentElement.offsetHeight - ) - window.innerHeight; // Subtract viewport height - } - - /** - * Calculates maximum possible width that can be scrolled. This helps avoid - * the constant increasing value of scrollWidth on IOS Safari. The - * continuous increase of width causes a crash on IOS Safari when the - * unnaturally high value of scrollWidth is passed to the scrollTo - * function. Therefore in the case where the scrollWidth parameter exceeds - * the maximum possible width value, the return value of this parameter is - * used instead. - * - * For additional info, visit: - * https://muffinman.io/ios-safari-get-bounding-client-rect-bug/ - * - * @returns {number} A number that represents the max width in pixels. - */ - function getPageMaxWidth() { - return Math.max( - document.body.scrollWidth, - document.body.offsetWidth, - document.documentElement.clientWidth, - document.documentElement.scrollWidth, - document.documentElement.offsetWidth - ) - window.innerWidth; // Subtract viewport width - } - - /* - * Reflow layout and send size events on resize/scroll - */ - - var last_scroll_left = 0; - var last_scroll_top = 0; - var last_scroll_width = 0; - var last_scroll_height = 0; - var last_window_width = 0; - var last_window_height = 0; - - function __update_layout() { - - // Only reflow if size or scroll have changed - if (document.body.scrollLeft !== last_scroll_left - || document.body.scrollTop !== last_scroll_top - || document.body.scrollWidth !== last_scroll_width - || document.body.scrollHeight !== last_scroll_height - || window.innerWidth !== last_window_width - || window.innerHeight !== last_window_height) { - - last_scroll_top = document.body.scrollTop; - last_scroll_left = document.body.scrollLeft; - last_scroll_width = document.body.scrollWidth; - last_scroll_height = document.body.scrollHeight; - last_window_width = window.innerWidth; - last_window_height = window.innerHeight; - - // Get appropriate scrolling height (not greater than max value) - var maxHeight = getPageMaxHeight(); - var scrollHeight = Math.min(document.body.scrollHeight, maxHeight); - - // Get appropriate scrolling width (not greater than max value) - var maxWidth = getPageMaxWidth(); - var scrollWidth = Math.min(document.body.scrollWidth, maxWidth); - - // Reset scroll and reposition document such that it's on-screen - window.scrollTo(scrollWidth, scrollHeight); - - // Determine height of bottom section (currently only text input) - var bottom = GuacUI.Client.text_input.container; - var bottom_height = (bottom && bottom.offsetHeight) | 0; - - // Calculate correct height of main section (display) - var main_width = window.innerWidth; - var main_height = window.innerHeight - bottom_height; - - // Anchor main to top-left of viewport, sized to fit above bottom - var main = GuacUI.Client.main; - main.style.top = document.body.scrollTop + "px"; - main.style.left = document.body.scrollLeft + "px"; - main.style.width = main_width + "px"; - main.style.height = main_height + "px"; - - // Anchor bottom to bottom of viewport - if (bottom) { - bottom.style.top = (document.body.scrollTop + main_height) + "px"; - bottom.style.left = document.body.scrollLeft + "px"; - bottom.style.width = window.innerWidth + "px"; - } - - // Send new size - if (GuacUI.Client.attachedClient) { - var pixel_density = window.devicePixelRatio || 1; - var width = main_width * pixel_density; - var height = main_height * pixel_density; - GuacUI.Client.attachedClient.sendSize(width, height); - } - - // Rescale display appropriately - GuacUI.Client.updateDisplayScale(); - - } - - } - - window.onresize = __update_layout; - window.onscroll = __update_layout; - window.setInterval(__update_layout, 10); - - GuacamoleSessionStorage.addChangeListener(function(name, value) { - if (name === "clipboard") { - GuacUI.Client.clipboard.value = value; - GuacUI.Client.setClipboard(value); - } - }); - - /** - * Ignores the given event. - * - * @private - * @param {Event} e The event to ignore. - */ - function _ignore(e) { - e.preventDefault(); - e.stopPropagation(); - } - - /** - * Converts the given bytes to a base64-encoded string. - * - * @private - * @param {Uint8Array} bytes A Uint8Array which contains the data to be - * encoded as base64. - * @return {String} The base64-encoded string. - */ - function _get_base64(bytes) { - - var data = ""; - - // Produce binary string from bytes in buffer - for (var i=0; i= bytes.length) { - stream.sendEnd(); - GuacUI.Client.notification_area.removeChild(upload.getElement()); - GuacUI.Client.showNotification("Upload of \"" + file.name + "\" complete."); - } - - // Otherwise, update progress - else - upload.updateProgress(GuacUI.Client.getSizeString(offset), offset / bytes.length * 100); - - }; - - // Close dialog and abort when close is clicked - upload.onclose = function() { - GuacUI.Client.notification_area.removeChild(upload.getElement()); - // TODO: Abort transfer - }; - - }; - reader.readAsArrayBuffer(file); - - } - - // Handle and ignore dragenter/dragover - GuacUI.Client.display.addEventListener("dragenter", _ignore, false); - GuacUI.Client.display.addEventListener("dragover", _ignore, false); - - // File drop event handler - GuacUI.Client.display.addEventListener("drop", function(e) { - - e.preventDefault(); - e.stopPropagation(); - - // Ignore file drops if no attached client - if (!GuacUI.Client.attachedClient) return; - - // Upload each file - var files = e.dataTransfer.files; - for (var i=0; i= 64 && Math.abs(dy) < 32 && duration < 250) { - GuacUI.Client.showMenu(); - guac_drag.cancel(); - } - } - - // Hide menu if swiping left - else if (GuacUI.Client.isMenuShown()) { - - GuacUI.Client.menu.scrollLeft -= change_drag_dx; - GuacUI.Client.menu.scrollTop -= change_drag_dy; - - if (dx <= -64 && Math.abs(dy) < 32 && duration < 250) { - GuacUI.Client.showMenu(false); - guac_drag.cancel(); - } - - } - - // Otherwise, drag UI (if not relative emulation) - else if (GuacUI.Client.emulate_absolute) { - GuacUI.Client.main.scrollLeft -= change_drag_dx; - GuacUI.Client.main.scrollTop -= change_drag_dy; - } - - last_drag_dx = dx; - last_drag_dy = dy; - - } - }; - - /* - * Initialize clipboard with current data - */ - - GuacUI.Client.clipboard.value = GuacamoleSessionStorage.getItem("clipboard", ""); - - /* - * Update clipboard contents when changed - */ - - window.onblur = - GuacUI.Client.clipboard.onchange = function() { - GuacUI.Client.commitClipboard(); - }; - - /* - * Update emulation mode when changed - */ - - GuacUI.Client.absolute_radio.onclick = - GuacUI.Client.absolute_radio.onchange = function() { - if (!GuacUI.Client.emulate_absolute) { - GuacUI.Client.showNotification("Absolute mouse emulation selected"); - GuacUI.Client.setMouseEmulationAbsolute(GuacUI.Client.absolute_radio.checked); - GuacUI.Client.showMenu(false); - } - }; - - GuacUI.Client.relative_radio.onclick = - GuacUI.Client.relative_radio.onchange = function() { - if (GuacUI.Client.emulate_absolute) { - GuacUI.Client.showNotification("Relative mouse emulation selected"); - GuacUI.Client.setMouseEmulationAbsolute(!GuacUI.Client.relative_radio.checked); - GuacUI.Client.showMenu(false); - } - }; - - /* - * Update input method mode when changed - */ - - GuacUI.Client.ime_none_radio.onclick = - GuacUI.Client.ime_none_radio.onchange = function() { - GuacUI.Client.showTextInput(false); - GuacUI.Client.OnScreenKeyboard.hide(); - GuacUI.Client.showMenu(false); - }; - - GuacUI.Client.ime_text_radio.onclick = - GuacUI.Client.ime_text_radio.onchange = function() { - GuacUI.Client.showTextInput(true); - GuacUI.Client.OnScreenKeyboard.hide(); - GuacUI.Client.showMenu(false); - }; - - GuacUI.Client.ime_osk_radio.onclick = - GuacUI.Client.ime_osk_radio.onchange = function() { - GuacUI.Client.showTextInput(false); - GuacUI.Client.OnScreenKeyboard.show(); - GuacUI.Client.showMenu(false); - }; - - /* - * Text input - */ - - // Disable automatic input features on platforms that support these attributes - GuacUI.Client.text_input.target.setAttribute("autocapitalize", "off"); - GuacUI.Client.text_input.target.setAttribute("autocorrect", "off"); - GuacUI.Client.text_input.target.setAttribute("autocomplete", "off"); - GuacUI.Client.text_input.target.setAttribute("spellcheck", "off"); - - function keysym_from_codepoint(codepoint) { - - // Keysyms for control characters - if (codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F)) - return 0xFF00 | codepoint; - - // Keysyms for ASCII chars - if (codepoint >= 0x0000 && codepoint <= 0x00FF) - return codepoint; - - // Keysyms for Unicode - if (codepoint >= 0x0100 && codepoint <= 0x10FFFF) - return 0x01000000 | codepoint; - - return null; - - } - - /** - * Presses and releases the key corresponding to the given keysym, as if - * typed by the user. - * - * @param {Number} keysym The keysym of the key to send. - */ - function send_keysym(keysym) { - - var guac = GuacUI.Client.attachedClient; - if (!guac) - return; - - guac.sendKeyEvent(1, keysym); - guac.sendKeyEvent(0, keysym); - - } - - /** - * Presses and releases the key having the keysym corresponding to the - * Unicode codepoint given, as if typed by the user. - * - * @param {Number} codepoint The Unicode codepoint of the key to send. - */ - function send_codepoint(codepoint) { - - if (codepoint === 10) { - send_keysym(0xFF0D); - release_sticky_keys(); - return; - } - - var keysym = keysym_from_codepoint(codepoint); - if (keysym) { - send_keysym(keysym); - release_sticky_keys(); - } - - } - - /** - * Translates each character within the given string to keysyms and sends - * each, in order, as if typed by the user. - * - * @param {String} content The string to send. - */ - function send_string(content) { - - var sent_text = ""; - - for (var i=0; i - */ - var active_sticky_keys = {}; - - /** - * Presses/releases the keysym defined by the "data-keysym" attribute on - * the given element whenever the element is pressed. The "data-sticky" - * attribute, if present and set to "true", causes the key to remain - * pressed until text is sent. - * - * @param {Element} key The element which will control its associated key. - */ - function apply_key_behavior(key) { - - function __update_key(e) { - - var guac = GuacUI.Client.attachedClient; - if (!guac) - return; - - e.preventDefault(); - e.stopPropagation(); - - // Pull properties of key - var keysym = parseInt(key.getAttribute("data-keysym")); - var sticky = (key.getAttribute("data-sticky") === "true"); - var pressed = (key.className.indexOf("pressed") !== -1); - - // If sticky, toggle pressed state - if (sticky) { - if (pressed) { - GuacUI.removeClass(key, "pressed"); - guac.sendKeyEvent(0, keysym); - delete active_sticky_keys[keysym]; - } - else { - GuacUI.addClass(key, "pressed"); - guac.sendKeyEvent(1, keysym); - active_sticky_keys[keysym] = key; - } - } - - // For all non-sticky keys, press and release key immediately - else - send_keysym(keysym); - - } - - var ignore_mouse = false; - - // Press/release key when clicked - key.addEventListener("click", function __mouse_key(e) { - - // Ignore clicks which follow touches - if (ignore_mouse) - return; - - __update_key(e); - - }, false); - - // Press/release key when tapped - key.addEventListener("touchstart", function __touch_key(e) { - - // Ignore following clicks - ignore_mouse = true; - - __update_key(e); - - }, false); - - // Restore handling of mouse events when mouse is used - key.addEventListener("mousemove", function __reset_mouse() { - ignore_mouse = false; - }, false); - - } - - /** - * Releases all currently-held sticky keys within the text input UI. - */ - function release_sticky_keys() { - - var guac = GuacUI.Client.attachedClient; - if (!guac) - return; - - // Release all active sticky keys - for (var keysym in active_sticky_keys) { - var key = active_sticky_keys[keysym]; - GuacUI.removeClass(key, "pressed"); - guac.sendKeyEvent(0, keysym); - } - - // Reset set of active keys - active_sticky_keys = {}; - - } - - // Apply key behavior to all keys within the text input UI - var keys = GuacUI.Client.text_input.container.getElementsByClassName("key"); - for (i=0; i 1 ? end : this.data.length) - , type - , this.encoding - ); - }; - FB_proto.toString = function() { - return "[object Blob]"; - }; - return FakeBlobBuilder; - }(view)); - - return function Blob(blobParts, options) { - var type = options ? (options.type || "") : ""; - var builder = new BlobBuilder(); - if (blobParts) { - for (var i = 0, len = blobParts.length; i < len; i++) { - builder.append(blobParts[i]); - } - } - return builder.getBlob(type); - }; -}(self)); diff --git a/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/LICENSE.md b/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/LICENSE.md deleted file mode 100644 index 7eb56b98..00000000 --- a/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/LICENSE.md +++ /dev/null @@ -1,30 +0,0 @@ -This software is licensed under the MIT/X11 license. - -MIT/X11 license ---------------- - -Copyright © 2011 [Eli Grey][1]. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - - - [1]: http://eligrey.com \ No newline at end of file diff --git a/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/filesaver.js b/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/filesaver.js deleted file mode 100644 index 1d858c5a..00000000 --- a/guacamole-tunnel/src/main/webapp/scripts/lib/filesaver/filesaver.js +++ /dev/null @@ -1,216 +0,0 @@ -/* FileSaver.js - * A saveAs() FileSaver implementation. - * 2013-01-23 - * - * By Eli Grey, http://eligrey.com - * License: X11/MIT - * See LICENSE.md - */ - -/*global self */ -/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, - plusplus: true */ - -/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ - -var saveAs = saveAs - || (navigator.msSaveBlob && navigator.msSaveBlob.bind(navigator)) - || (function(view) { - "use strict"; - var - doc = view.document - // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet - , get_URL = function() { - return view.URL || view.webkitURL || view; - } - , URL = view.URL || view.webkitURL || view - , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") - , can_use_save_link = "download" in save_link - , click = function(node) { - var event = doc.createEvent("MouseEvents"); - event.initMouseEvent( - "click", true, false, view, 0, 0, 0, 0, 0 - , false, false, false, false, 0, null - ); - node.dispatchEvent(event); - } - , webkit_req_fs = view.webkitRequestFileSystem - , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem - , throw_outside = function (ex) { - (view.setImmediate || view.setTimeout)(function() { - throw ex; - }, 0); - } - , force_saveable_type = "application/octet-stream" - , fs_min_size = 0 - , deletion_queue = [] - , process_deletion_queue = function() { - var i = deletion_queue.length; - while (i--) { - var file = deletion_queue[i]; - if (typeof file === "string") { // file is an object URL - URL.revokeObjectURL(file); - } else { // file is a File - file.remove(); - } - } - deletion_queue.length = 0; // clear queue - } - , dispatch = function(filesaver, event_types, event) { - event_types = [].concat(event_types); - var i = event_types.length; - while (i--) { - var listener = filesaver["on" + event_types[i]]; - if (typeof listener === "function") { - try { - listener.call(filesaver, event || filesaver); - } catch (ex) { - throw_outside(ex); - } - } - } - } - , FileSaver = function(blob, name) { - // First try a.download, then web filesystem, then object URLs - var - filesaver = this - , type = blob.type - , blob_changed = false - , object_url - , target_view - , get_object_url = function() { - var object_url = get_URL().createObjectURL(blob); - deletion_queue.push(object_url); - return object_url; - } - , dispatch_all = function() { - dispatch(filesaver, "writestart progress write writeend".split(" ")); - } - // on any filesys errors revert to saving with object URLs - , fs_error = function() { - // don't create more object URLs than needed - if (blob_changed || !object_url) { - object_url = get_object_url(blob); - } - if (target_view) { - target_view.location.href = object_url; - } else { - window.open(object_url, "_blank"); - } - filesaver.readyState = filesaver.DONE; - dispatch_all(); - } - , abortable = function(func) { - return function() { - if (filesaver.readyState !== filesaver.DONE) { - return func.apply(this, arguments); - } - }; - } - , create_if_not_found = {create: true, exclusive: false} - , slice - ; - filesaver.readyState = filesaver.INIT; - if (!name) { - name = "download"; - } - if (can_use_save_link) { - object_url = get_object_url(blob); - save_link.href = object_url; - save_link.download = name; - click(save_link); - filesaver.readyState = filesaver.DONE; - dispatch_all(); - return; - } - // Object and web filesystem URLs have a problem saving in Google Chrome when - // viewed in a tab, so I force save with application/octet-stream - // http://code.google.com/p/chromium/issues/detail?id=91158 - if (view.chrome && type && type !== force_saveable_type) { - slice = blob.slice || blob.webkitSlice; - blob = slice.call(blob, 0, blob.size, force_saveable_type); - blob_changed = true; - } - // Since I can't be sure that the guessed media type will trigger a download - // in WebKit, I append .download to the filename. - // https://bugs.webkit.org/show_bug.cgi?id=65440 - if (webkit_req_fs && name !== "download") { - name += ".download"; - } - if (type === force_saveable_type || webkit_req_fs) { - target_view = view; - } - if (!req_fs) { - fs_error(); - return; - } - fs_min_size += blob.size; - req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { - fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { - var save = function() { - dir.getFile(name, create_if_not_found, abortable(function(file) { - file.createWriter(abortable(function(writer) { - writer.onwriteend = function(event) { - target_view.location.href = file.toURL(); - deletion_queue.push(file); - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "writeend", event); - }; - writer.onerror = function() { - var error = writer.error; - if (error.code !== error.ABORT_ERR) { - fs_error(); - } - }; - "writestart progress write abort".split(" ").forEach(function(event) { - writer["on" + event] = filesaver["on" + event]; - }); - writer.write(blob); - filesaver.abort = function() { - writer.abort(); - filesaver.readyState = filesaver.DONE; - }; - filesaver.readyState = filesaver.WRITING; - }), fs_error); - }), fs_error); - }; - dir.getFile(name, {create: false}, abortable(function(file) { - // delete file if it already exists - file.remove(); - save(); - }), abortable(function(ex) { - if (ex.code === ex.NOT_FOUND_ERR) { - save(); - } else { - fs_error(); - } - })); - }), fs_error); - }), fs_error); - } - , FS_proto = FileSaver.prototype - , saveAs = function(blob, name) { - return new FileSaver(blob, name); - } - ; - FS_proto.abort = function() { - var filesaver = this; - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "abort"); - }; - FS_proto.readyState = FS_proto.INIT = 0; - FS_proto.WRITING = 1; - FS_proto.DONE = 2; - - FS_proto.error = - FS_proto.onwritestart = - FS_proto.onprogress = - FS_proto.onwrite = - FS_proto.onabort = - FS_proto.onerror = - FS_proto.onwriteend = - null; - - view.addEventListener("unload", process_deletion_queue, false); - return saveAs; -}(self)); diff --git a/guacamole-tunnel/src/main/webapp/scripts/session.js b/guacamole-tunnel/src/main/webapp/scripts/session.js deleted file mode 100644 index 69b9f979..00000000 --- a/guacamole-tunnel/src/main/webapp/scripts/session.js +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2017 Glyptodon, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/** - * Global storage for Guacamole pages. - */ -GuacamoleSessionStorage = new (function() { - - /** - * The contents of storage, as a JSON string containing name/value pairs as - * properties. - * - * @private - * @type String - */ - var stored_json = "{}"; - - /** - * Called whenever an item value changes. - * - * @callback onchange - * @param {String} name The name of the item changed. - * @param value The new item value. - */ - - /** - * All attached listeners. - * - * @type onchange[] - */ - var listeners = []; - - /** - * Notifies all listeners that an item has changed. - * - * @param {String} name The name of the item that changed. - * @param value The new item value. - */ - function __notify_changed(name, value) { - for (var i=0; i * { - margin-left: auto; - margin-right: auto; -} - -div.magnifier-background { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 1; - overflow: hidden; -} - -div.magnifier { - - position: absolute; - left: 0; - top: 0; - - box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.75); - width: 50%; - height: 50%; - overflow: hidden; - -} - -.pan-overlay, -.type-overlay { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - z-index: 1; -} - -.pan-overlay .indicator { - position: fixed; - background-size: 32px 32px; - -moz-background-size: 32px 32px; - -webkit-background-size: 32px 32px; - -khtml-background-size: 32px 32px; - background-position: center; - background-repeat: no-repeat; - opacity: 0.8; -} - -.pan-overlay .indicator.up { - - top: 0; - left: 0; - right: 0; - height: 32px; - - background-image: url('../images/arrows/arrows-u.png'); - -} - -.pan-overlay .indicator.down { - - bottom: 0; - left: 0; - right: 0; - height: 32px; - - background-image: url('../images/arrows/arrows-d.png'); - -} - -.pan-overlay .indicator.left { - - top: 0; - bottom: 0; - left: 0; - width: 32px; - - background-image: url('../images/arrows/arrows-l.png'); - -} - -.pan-overlay .indicator.right { - - top: 0; - bottom: 0; - right: 0; - width: 32px; - - background-image: url('../images/arrows/arrows-r.png'); - -} - -/* Viewport Clone */ - -div#viewportClone { - display: table; - height: 100%; - width: 100%; - position: fixed; - left: 0; - top: 0; - - visibility: hidden; -} - -@keyframes show-dialog { - 0% {transform: scale(0.75); } - 100% {transform: scale(1); } -} - -@-webkit-keyframes show-dialog { - 0% {-webkit-transform: scale(0.75); } - 100% {-webkit-transform: scale(1); } -} - -.dialog { - - animation-name: show-dialog; - animation-timing-function: linear; - animation-duration: 0.125s; - -webkit-animation-name: show-dialog; - -webkit-animation-timing-function: linear; - -webkit-animation-duration: 0.125s; - - max-width: 75%; - max-height: none; - width: 4in; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - - padding: 0.5em; - text-align: left; - -} - -.guac-error .dialog { - background: #FDD; - border: 1px solid #964040; -} - -.dialog .title { - font-size: 1.1em; - font-weight: bold; - border-bottom: 1px solid black; - margin-bottom: 0.5em; -} - -.dialog .status, .dialog .countdown, .dialog .reconnect { - margin: 0; - padding: 0.5em; - font-size: 0.8em; -} - -.dialog .reconnect { - display: none; -} - -.guac-error .dialog .reconnect { - display: block; - text-align: center; -} - -p.hint { - - border: 0.25em solid rgba(255, 255, 255, 0.25); - background: black; - opacity: 0.75; - - color: white; - - max-width: 10em; - padding: 1em; - margin: 1em; - - position: absolute; - left: 0; - top: 0; - - box-shadow: 0.25em 0.25em 0.25em rgba(0, 0, 0, 0.75); - -} - -#notificationArea { - position: fixed; - right: 0.5em; - bottom: 0.5em; - max-width: 25%; - min-width: 10em; -} - -.notification { - - font-size: 0.7em; - text-align: center; - - border: 1px solid rgba(0, 0, 0, 0.75); - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - background: white; - - color: black; - - padding: 0.5em; - margin: 1em; - overflow: hidden; - - box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.25); - -} - -.notification div { - display: inline-block; - text-align: left; -} - -.notification .title-bar { - display: block; - white-space: nowrap; - font-weight: bold; - - border-bottom: 1px solid black; - padding-bottom: 0.5em; - margin-bottom: 0.5em; -} - -.notification .title-bar * { - vertical-align: middle; -} - -.notification .close { - - background: url('../images/action-icons/guac-close.png'); - background-size: 10px 10px; - -moz-background-size: 10px 10px; - -webkit-background-size: 10px 10px; - -khtml-background-size: 10px 10px; - - width: 10px; - height: 10px; - - float: right; - cursor: pointer; - -} - -@keyframes progress { - from {background-position: 0px 0px;} - to {background-position: 64px 0px;} -} - -@-webkit-keyframes progress { - from {background-position: 0px 0px;} - to {background-position: 64px 0px;} -} - -.notification .caption, -.download.notification .caption { - width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.upload.notification .status, -.download.notification .status { - color: red; - font-size: 1em; - padding: 1em; -} - -.download.notification .progress, -.upload.notification .progress, -.download.notification .download { - - margin-top: 1em; - margin-left: 0.75em; - padding: 0.25em; - min-width: 5em; - - border: 1px solid gray; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - - text-align: center; - float: right; - - position: relative; - -} - -.upload.notification .progress { - float: none; - width: 80%; - margin-left: auto; - margin-right: auto; -} - -.download.notification .progress div, -.upload.notification .progress div { - position: relative; -} - -.download.notification .progress .bar, -.upload.notification .progress .bar { - background: #A3D655; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 0; - box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.5), - inset -1px -1px 0 rgba( 0, 0, 0, 0.1), - 1px 1px 0 gray; -} - -.upload.notification .progress, -.download.notification .progress { - - background: #C2C2C2 url('../images/progress.png'); - background-size: 16px 16px; - -moz-background-size: 16px 16px; - -webkit-background-size: 16px 16px; - -khtml-background-size: 16px 16px; - - animation-name: progress; - animation-duration: 2s; - animation-timing-function: linear; - animation-iteration-count: infinite; - - -webkit-animation-name: progress; - -webkit-animation-duration: 2s; - -webkit-animation-timing-function: linear; - -webkit-animation-iteration-count: infinite; - -} - -.download.notification .download { - background: rgb(16, 87, 153); - cursor: pointer; -} - -#preload { - visibility: hidden; - position: absolute; - left: 0; - right: 0; - width: 0; - height: 0; - overflow: hidden; -} - -/* Menu */ - -/** - * show-menu: Animation for showing the menu - */ -@keyframes show-menu { - from { left: -480px; opacity: 0;} - to { left: 0; opacity: 1;} -} -@-moz-keyframes show-menu { - from { left: -480px; opacity: 0;} - to { left: 0; opacity: 1;} -} -@-webkit-keyframes show-menu { - from { left: -480px; opacity: 0;} - to { left: 0; opacity: 1;} -} - -/** - * hide-menu: Animation for hiding the menu - */ -@keyframes hide-menu { - from { left: 0; opacity: 1;} - to { left: -480px; opacity: 0;} -} -@-moz-keyframes hide-menu { - from { left: 0; opacity: 1;} - to { left: -480px; opacity: 0;} -} -@-webkit-keyframes hide-menu { - from { left: 0; opacity: 1;} - to { left: -480px; opacity: 0;} -} - -#menu { - overflow: auto; - position: fixed; - top: 0; - bottom: 0; - max-width: 100%; - width: 480px; - background: #EEE; - box-shadow: inset -1px 0 2px white, 1px 0 2px black; - z-index: 10; -} - -#menu .content { - padding: 1em; -} - -#menu .content > * { - margin: 1em 0; -} - -#menu, #menu.closed { - left: -480px; - opacity: 0; -} - -#menu.open { - left: 0px; - opacity: 1; -} - -#menu.closed { - animation-name: hide-menu; - animation-timing-function: linear; - animation-duration: 0.05s; - -webkit-animation-name: hide-menu; - -webkit-animation-timing-function: linear; - -webkit-animation-duration: 0.05s; -} - -#menu.open { - animation-name: show-menu; - animation-timing-function: linear; - animation-duration: 0.05s; - -webkit-animation-name: show-menu; - -webkit-animation-timing-function: linear; - -webkit-animation-duration: 0.05s; -} - -#clipboard-settings textarea { - width: 100%; - border: 1px solid #AAA; - -moz-border-radius: 0.25em; - -webkit-border-radius: 0.25em; - -khtml-border-radius: 0.25em; - border-radius: 0.25em; - white-space: pre; - display: block; -} - -#mouse-settings .choice { - text-align: center; -} - -#mouse-settings .choice .figure { - display: inline-block; - vertical-align: middle; - max-width: 80%; -} - -#keyboard-settings .caption { - font-size: 0.9em; - margin-left: 2em; - margin-right: 2em; -} - -#mouse-settings .figure .caption { - text-align: center; - font-size: 0.9em; -} - -#mouse-settings .figure img { - display: block; - max-width: 100%; - margin: 1em auto; -} - -#menu h3 { - - background: rgba(0, 0, 0, 0.4); - - padding: 0.25em 0.5em; - margin: 0; - font-size: 1em; - - color: white; - font-weight: bold; - -} - -#keyboard-settings .figure { - float: right; - max-width: 30%; - margin: 1em; -} - -#keyboard-settings .figure img { - max-width: 100%; -} - -#zoom-settings { - text-align: center; -} - -#zoom-out, #zoom-in, #zoom-state { - display: inline-block; - vertical-align: middle; -} - -#zoom-out, #zoom-in { - max-width: 3em; - border: 1px solid rgba(0, 0, 0, 0.5); - background: rgba(0, 0, 0, 0.1); - border-radius: 2em; - margin: 0.5em; - cursor: pointer; -} - -#zoom-out img, #zoom-in img { - max-width: 100%; - opacity: 0.5; -} - -#zoom-out:hover, #zoom-in:hover { - border: 1px solid rgba(0, 0, 0, 1); - background: #CDA; -} - -#zoom-out:hover img, #zoom-in:hover img { - opacity: 1; -} - -#zoom-state { - font-size: 2em; -} - -#text-input { - display: none; - position: absolute; - - border-top: 1px solid rgba(0, 0, 0, 0.5); - background: #CDA; - -} - -#text-input-field, -#text-input-buttons { - display: inline-block; - vertical-align: middle; -} - -#text-input-field { - width: 30%; - overflow: hidden; - white-space: nowrap; -} - -#text-input-buttons { - width: 70%; - text-align: right; -} - -#target { - - border: none; - border-radius: 0; - - display: inline-block; - vertical-align: middle; - font-size: 12pt; - width: 100%; - height: auto; - resize: none; - outline: none; - - margin: 0; - padding: 0.25em; - padding-left: 0; - background: #CDA; - overflow: hidden; - -} - -#text-input.open { - display: block; -} - -#sent-history { - display: inline-block; - vertical-align: middle; - padding: 0.25em; - padding-right: 0; -} - -#sent-history .sent-text { - display: inline-block; - vertical-align: baseline; - white-space: pre; - font-size: 12pt; - - animation: fadeout 1s linear; - -webkit-animation: fadeout 1s linear; - opacity: 0; -} - -#text-input-buttons button { - border: 1px solid rgba(0, 0, 0, 0.5); - background: none; - color: black; - box-shadow: none; - text-shadow: none; - padding: 0.25em; - max-width: 20%; - margin: 0.1em; - min-width: 3em; -} - -#text-input-buttons button:active, -#text-input-buttons button.pressed { - border: 1px solid rgba(255, 255, 255, 0.5); - background: rgba(0, 0, 0, 0.75); - color: white; -} - -.notification.message { - background: #DFD; - animation: fadein 0.125s linear, fadeout 2s 3s linear; - -webkit-animation: fadein 0.125s linear, fadeout 2s 3s linear; -} - -.notification.message .caption { - vertical-align: middle; - white-space: normal; - overflow: hidden; - text-overflow: ellipsis; -} - diff --git a/guacamole-tunnel/src/main/webapp/styles/keyboard.css b/guacamole-tunnel/src/main/webapp/styles/keyboard.css deleted file mode 100644 index 3e62bfda..00000000 --- a/guacamole-tunnel/src/main/webapp/styles/keyboard.css +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2017 Glyptodon, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -.keyboard-container { - text-align: center; - - position: fixed; - left: 0; - bottom: 0; - width: 100%; - margin: 0; - padding: 0; - - border-top: 1px solid black; - background: #222; - opacity: 0.85; - - z-index: 1; -} - -.guac-keyboard { - display: inline-block; - width: 100%; - - margin: 0; - padding: 0; - cursor: default; - - text-align: left; - vertical-align: middle; -} - -.guac-keyboard .guac-keyboard-key-container { - display: inline-block; -} - -.guac-keyboard .guac-keyboard-key { - background: #444; - border: 1px outset #888; - -moz-border-radius: 0.1em; - -webkit-border-radius: 0.1em; - -khtml-border-radius: 0.1em; - border-radius: 0.1em; -} - -.guac-keyboard .guac-keyboard-cap { - color: white; - font-family: sans-serif; - font-size: 50%; - font-weight: lighter; - text-align: center; - white-space: pre; -} - -.guac-keyboard .guac-keyboard-key:hover { - cursor: pointer; -} - -.guac-keyboard .guac-keyboard-key.highlight { - background: #666; - border-color: #666; -} - -.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key.shift, -.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym { - background: #882; - border-color: #DD4; -} - -.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key.control, -.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym { - background: #882; - border-color: #DD4; -} - -.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key.alt, -.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym { - background: #882; - border-color: #DD4; -} - -.guac-keyboard.guac-keyboard-modifier-super .guac-keyboard-key.super, -.guac-keyboard.guac-keyboard-modifier-numsym .guac-keyboard-key.numsym { - background: #882; - border-color: #DD4; -} - -.guac-keyboard .guac-keyboard-key.guac-keyboard-pressed { - background: #822; - border-color: #D44; - border-style: inset; -} - -.guac-keyboard .guac-keyboard-row { - line-height: 0; -} - -.guac-keyboard .guac-keyboard-column { - display: inline-block; - text-align: center; - vertical-align: top; -} - -.guac-keyboard .guac-keyboard-gap { - display: inline-block; -} - -/* Hide keycaps requiring modifiers which are NOT currently active. */ -.guac-keyboard:not(.guac-keyboard-modifier-caps) -.guac-keyboard-cap.guac-keyboard-requires-caps, - -.guac-keyboard:not(.guac-keyboard-modifier-numsym) -.guac-keyboard-cap.guac-keyboard-requires-numsym, - -.guac-keyboard:not(.guac-keyboard-modifier-shift) -.guac-keyboard-cap.guac-keyboard-requires-shift, - -/* Hide keycaps NOT requiring modifiers which ARE currently active, where that - modifier is used to determine which cap is displayed for the current key. */ -.guac-keyboard.guac-keyboard-modifier-shift -.guac-keyboard-key.guac-keyboard-uses-shift -.guac-keyboard-cap:not(.guac-keyboard-requires-shift), - -.guac-keyboard.guac-keyboard-modifier-numsym -.guac-keyboard-key.guac-keyboard-uses-numsym -.guac-keyboard-cap:not(.guac-keyboard-requires-numsym), - -.guac-keyboard.guac-keyboard-modifier-caps -.guac-keyboard-key.guac-keyboard-uses-caps -.guac-keyboard-cap:not(.guac-keyboard-requires-caps) { - - display: none; - -} diff --git a/guacamole-tunnel/src/main/webapp/styles/ui.css b/guacamole-tunnel/src/main/webapp/styles/ui.css deleted file mode 100644 index 2633d4d9..00000000 --- a/guacamole-tunnel/src/main/webapp/styles/ui.css +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2017 Glyptodon, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -@import url('animation.css'); - -* { - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - -input[type=checkbox], input[type=number], input[type=text], input[type=radio], label, textarea { - -webkit-tap-highlight-color: rgba(128,192,128,0.5); -} - -input[type=submit], button { - -webkit-appearance: none; -} - -div.location, input[type=text], input[type=number], input[type=password], textarea { - border: 1px solid #777; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - width: 100%; - max-width: 16em; - padding: 0.25em; - font-size: 10pt; - background: white; - cursor: text; -} - -textarea { - max-width: none; - width: 30em; - height: 10em; - white-space: nowrap; - overflow: auto; -} - -input[type="submit"], button { - - background-color: #3C3C3C; - - border: 1px solid rgba(0, 0, 0, 0.4); - -moz-border-radius: 0.25em; - -webkit-border-radius: 0.25em; - -khtml-border-radius: 0.25em; - border-radius: 0.25em; - - color: white; - text-shadow: -1px -1px rgba(0, 0, 0, 0.3); - font-size: 1em; - - box-shadow: inset -1px -1px 0.1em rgba(0, 0, 0, 0.25), - inset 1px 1px 0.1em rgba(255, 255, 255, 0.25), - -1px -1px 0.1em rgba(0, 0, 0, 0.25), - 1px 1px 0.1em rgba(255, 255, 255, 0.25); - - padding: 0.35em; - padding-right: 1em; - padding-left: 1em; - min-width: 5em; - -} - -input[type="submit"]:hover, button:hover { - background-color: #5A5A5A; -} - -input[type="submit"]:active, button:active { - - background-color: #2C2C2C; - - box-shadow: - inset 1px 1px 0.25em rgba(0, 0, 0, 0.25), - -1px -1px 0.25em rgba(0, 0, 0, 0.25), - 1px 1px 0.25em rgba(255, 255, 255, 0.25); -} - -button.danger { - background: #A43; -} - -button.danger:hover { - background: #C54; -} - -button.danger:active { - background: #932; -} - -body { - background: #EEE; - font-family: FreeSans, Helvetica, Arial, sans-serif; - padding: 0; - margin: 0; -} - -img { - border: none; - vertical-align: middle; -} - -h1 { - - margin: 0; - padding: 0.5em; - - font-size: 2em; - vertical-align: middle; - text-align: center; - -} - -h2 { - - border-top: 1px solid #AAA; - border-bottom: 1px solid #AAA; - background: rgba(0, 0, 0, 0.07); - - padding: 0.5em; - margin: 0; - font-size: 1.5em; - - font-weight: lighter; - text-shadow: 1px 1px white; - -} - -div.section { - margin: 0; - padding: 1em; -} - -/* - * Dialogs - */ - -.dialog-container { - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - background: rgba(0, 0, 0, 0.5); - padding: 1em; -} - -.dialog { - - max-width: 100%; - width: 8in; - margin-left: auto; - margin-right: auto; - max-height: 100%; - overflow: auto; - - border: 1px solid rgba(0, 0, 0, 0.5); - background: #E7E7E7; - - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - - box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.6); - -} - -.dialog > * { - margin: 1em; -} - -.dialog .header { - margin: 0; -} - -.dialog td { - position: relative; -} - -.dialog .overlay { - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 1; -} - -.dialog .dropdown { - - position: absolute; - z-index: 2; - margin-top: -1px; - - width: 3in; - max-height: 2in; - overflow: auto; - - border: 1px solid rgba(0, 0, 0, 0.5); - background: white; - - font-size: 10pt; - -} - -.dialog .footer { - text-align: center; -} - -/* - * List elements - */ - -.list-item { - - display: block; - text-align: left; - cursor: pointer; - - position: relative; - -} - -.icon { - width: 24px; - height: 24px; - background-size: 16px 16px; - -moz-background-size: 16px 16px; - -webkit-background-size: 16px 16px; - -khtml-background-size: 16px 16px; - background-repeat: no-repeat; - background-position: center center; - opacity: 0.5; - display: inline-block; - vertical-align: middle; -} - -.list-item * { - vertical-align: middle; -} - -.list-item .caption { - padding: 0.1em; -} - -.list-item .name { - color: black; - font-weight: normal; - padding: 0.1em; - margin-left: 0.25em; -} - -.list-item .usage { - float: right; - font-style: italic; - color: gray; -} - -.list-item.in-use { - opacity: 0.5; -} - -.choice .list-item .usage { - display: none; -} - -.choice .list-item.in-use { - opacity: 1; -} - -/* - * List element styling - */ - -.list-item.selected { - background: #DEB; -} - -.list-item.selected > .icon { - opacity: 1.0; -} - -.list-item:not(.selected) .caption:hover { - background: #CDA; -} - -.choice .list-item { - display: inline-block; -} - -.choice input[type='checkbox'] { - vertical-align: top; - height: 24px; - padding: 0; - margin: 0; -} - -.disabled .list-item:not(.selected) { - opacity: 0.25; -} - -.disabled .list-item:not(.selected):hover { - background: inherit; -} - -/* - * List element fields (editing) - */ - -/* -.form { - - position: absolute; - display: inline-block; - vertical-align: middle; - z-index: 1; - - border: 1px solid rgba(0, 0, 0, 0.5); - background: #E7E7E7; - padding: 0; - margin: 0.25em; - - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; - - box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.6); - -} -*/ - -.form .fields th, -.form .permissions th { - font-weight: normal; - vertical-align: middle; - text-align: left; -} - -.form h2 { - border-top: none; -} - -.form h3 { - font-size: 1em; - margin-bottom: 0.25em; -} - -.form { - cursor: auto; - animation-name: fadein; - -webkit-animation-name: fadein; - animation-duration: 0.125s; - -webkit-animation-duration: 0.125s; -} - -.object-buttons { - - text-align: right; - - border-top: 1px solid rgba(0, 0, 0, 0.1); - padding-top: 0.5em; - margin: 0.5em; - -} - -/* - * List element icons - */ - -.icon.user { - background-image: url('../images/user-icons/guac-user.png'); -} - -.icon.user.add { - background-image: url('../images/action-icons/guac-user-add.png'); -} - -.icon.connection { - background-image: url('../images/protocol-icons/guac-plug.png'); -} - -.icon.connection.add { - background-image: url('../images/action-icons/guac-monitor-add.png'); -} - -.protocol { - display: inline-block; -} - -.protocol .icon { - width: 24px; - height: 24px; - background-image: url('../images/protocol-icons/guac-plug.png'); - background-size: 16px 16px; - -moz-background-size: 16px 16px; - -webkit-background-size: 16px 16px; - -khtml-background-size: 16px 16px; - background-repeat: no-repeat; - background-position: center center; - opacity: 0.5; -} - -.protocol .icon.ssh, -.protocol .icon.telnet { - background-image: url('../images/protocol-icons/guac-text.png'); -} - -.protocol .icon.vnc, -.protocol .icon.rdp { - background-image: url('../images/protocol-icons/guac-monitor.png'); -} - -.connection .thumbnail { - display: none; -} - -/* - * Groups - */ - -.group > .children { - margin-left: 13px; - padding-left: 6px; - display: none; -} - -.group.expanded > .children { - display: block; - border-left: 1px dotted rgba(0, 0, 0, 0.25); -} - -.group > .caption .icon.type { - display: none; -} - -.group.balancer > .caption .icon.type { - display: inline-block; - background-image: url('../images/protocol-icons/guac-monitor.png'); -} - -.group > .caption .icon.group { - opacity: 0.75; - background-image: url('../images/group-icons/guac-closed.png'); -} - -.group.expanded > .caption .icon.group { - background-image: url('../images/group-icons/guac-open.png'); -} - -.group.empty > .caption .icon.group { - opacity: 0.25; - background-image: url('../images/group-icons/guac-open.png'); -} - -.group.empty.balancer > .caption .icon.group { - display: none; -} - -/* - * Settings formatting - */ - -.form dt, -.settings dt { - border-bottom: 1px dotted #AAA; - padding-bottom: 0.25em; -} - -.form dd, -.settings dd { - margin: 1.5em; - margin-left: 2.5em; - font-size: 0.75em; -} - -#connections input.name, -#users input.name { - max-width: 80%; - width: 20em; -} - -#connection-list, -#user-list { - border: 1px solid rgba(0, 0, 0, 0.25); - min-height: 20em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; -} - -#connections #add-connection, -#connections #add-connection-group, -#users #add-user { - font-size: 0.8em; -} - -#connection-add-form, -#user-add-form { - margin-bottom: 0.5em; -} - -body:not(.manage-connections) .require-manage-connections, -body:not(.manage-users) .require-manage-users { - display: none; -} - -body:not(.add-connections) #add-connection, -body:not(.add-connection-groups) #add-connection-group, -body:not(.add-users) #user-add-form { - display: none; - display: none; -} - -div#logout-panel { - padding: 0.45em; - text-align: right; - float: right; -} - -.history th, -.history td { - padding-left: 1em; - padding-right: 1em; -} - -.first-page, -.prev-page, -.set-page, -.next-page, -.last-page { - cursor: pointer; - vertical-align: middle; -} - -.first-page.disabled, -.prev-page.disabled, -.set-page.disabled, -.next-page.disabled, -.last-page.disabled { - cursor: auto; - opacity: 0.25; -} - -.set-page, -.more-pages { - display: inline-block; - padding: 0.25em; - text-align: center; - min-width: 1.25em; -} - -.set-page { - text-decoration: underline; -} - -.set-page.current { - cursor: auto; - text-decoration: none; - font-weight: bold; - background: rgba(0, 0, 0, 0.1); - border: 1px solid rgba(0, 0, 0, 0.1); - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; -} - -.icon.first-page { - background-image: url('../images/action-icons/guac-first-page.png'); -} - -.icon.prev-page { - background-image: url('../images/action-icons/guac-prev-page.png'); -} - -.icon.next-page { - background-image: url('../images/action-icons/guac-next-page.png'); -} - -.icon.last-page { - background-image: url('../images/action-icons/guac-last-page.png'); -} - -.buttons, -.list-pager-buttons { - text-align: center; - margin: 1em; -} - -button#logout { - - background-image: url('../images/action-icons/guac-logout.png'); - background-repeat: no-repeat; - background-size: 1em; - background-position: 0.5em 0.45em; - - padding-left: 1.8em; - -} -