diff --git a/guacamole/ChangeLog b/guacamole/ChangeLog new file mode 100644 index 000000000..af5a4ed53 --- /dev/null +++ b/guacamole/ChangeLog @@ -0,0 +1,23 @@ +2011-12-11 Michael Jumper + + * Improved UI usability + * Support for multiple connections per user + * Real support for authentication providers + * Logout button + * Connection type icons (thanks to Tango Desktop Project) + * Fixed Ctrl-Alt-Delete bug (ticket #57) + * Fixed arrow key rendering (Chrome-specific issue) + * Fixed exception in XMLReader.parse() (ticket #66) + +2011-07-13 Michael Jumper + + * Migrated to new tunnel API + * Major cleanup of UI + * Fixed corrupt mouse cursor image + * Improved JavaScript style + * Logging (via SLF4J) + +2011-03-02 Michael Jumper + + * Initial release of modern 0.3.0+ series + diff --git a/guacamole/README b/guacamole/README new file mode 100644 index 000000000..0ac4c4777 --- /dev/null +++ b/guacamole/README @@ -0,0 +1,70 @@ + +------------------------------------------------------------ + About this README +------------------------------------------------------------ + +This README is intended to provide quick and to-the-point documentation for +technical users intending to compile parts of Guacamole themselves. + +Distribution-specific packages are available from the files section of the main +project page: + + http://sourceforge.net/projects/guacamole/files/ + +Distribution-specific documentation is provided on the Guacamole wiki: + + http://guac-dev.org/ + + +------------------------------------------------------------ + What is Guacamole? +------------------------------------------------------------ + +Guacamole is an HTML5 web application that provides access to your desktop using +remote desktop protocols. A centralized server acts as a tunnel and proxy, +allowing access to multiple desktops through a web browser; no plugins needed. +The client requires nothing more than a web browser supporting HTML5 and AJAX. + +The Guacamole project maintains this web application and the Java and C +libraries and programs it depends on. These libraries and programs are +separate in order to enable others to implement other applications using the +same underlying technology. + +All components and dependencies of Guacamole are free and open source. + + +------------------------------------------------------------ + Compiling and installing Guacamole +------------------------------------------------------------ + +Guacamole is built using Maven. Building Guacamole compiles all classes and +packages them into a deployable .war file. This .war file can be installed +and deployed under servlet containers like Apache Tomcat or Jetty. + +1) Run mvn package + + $ mvn package + + Maven will download any needed dependencies for building the .jar file. + Once all dependencies have been downloaded, the .war file will be + created in the target/ subdirectory of the current directory. + +2) Copy the .war file as directed in the instructions provided with + your servlet container. + + Apache Tomcat, Jetty, and other servlet containers have specific and + varying locations that .war files must be placed for the web + application to be deployed. + + You will likely need to do this as root. + + +------------------------------------------------------------ + Reporting problems +------------------------------------------------------------ + +Please report any bugs encountered by opening a new ticket at the Trac system +hosted at: + + http://guac-dev.org/trac/ + diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java index 26c41b32c..0b5b6488f 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicFileAuthenticationProvider.java @@ -32,7 +32,7 @@ import java.util.HashMap; import java.util.Map; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.auth.UsernamePassword; -import net.sourceforge.guacamole.net.basic.properties.BasicGuacamoleProperties; +import net.sourceforge.guacamole.properties.FileGuacamoleProperty; import net.sourceforge.guacamole.properties.GuacamoleProperties; import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; import org.slf4j.Logger; @@ -44,6 +44,13 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; +/** + * Authenticates users against a static list of username/password pairs. + * Each username/password may be associated with exactly one configuration. + * This list is stored in an XML file which is reread if modified. + * + * @author Michael Jumper + */ public class BasicFileAuthenticationProvider implements AuthenticationProvider { private Logger logger = LoggerFactory.getLogger(BasicFileAuthenticationProvider.class); @@ -51,10 +58,20 @@ public class BasicFileAuthenticationProvider implements AuthenticationProvider mapping; + /** + * The filename of the XML file to read the user mapping from. + */ + public static final FileGuacamoleProperty BASIC_USER_MAPPING = new FileGuacamoleProperty() { + + @Override + public String getName() { return "basic-user-mapping"; } + + }; + private File getUserMappingFile() throws GuacamoleException { // Get user mapping file - return GuacamoleProperties.getProperty(BasicGuacamoleProperties.BASIC_USER_MAPPING); + return GuacamoleProperties.getProperty(BASIC_USER_MAPPING); } diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java index b1376f2db..44123c305 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicGuacamoleTunnelServlet.java @@ -33,6 +33,12 @@ import net.sourceforge.guacamole.servlet.GuacamoleHTTPTunnelServlet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Connects users to a tunnel associated with the authorized configuration + * having the given ID. + * + * @author Michael Jumper + */ public class BasicGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet { private Logger logger = LoggerFactory.getLogger(BasicGuacamoleTunnelServlet.class); @@ -51,7 +57,7 @@ public class BasicGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet { // If no configs in session, not authorized if (configs == null) - throw new GuacamoleException("No authorized configurations."); + throw new GuacamoleException("Cannot connect - user not logged in."); // Get authorized config GuacamoleConfiguration config = configs.get(id); diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java index 34a72e798..6fe2f106e 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogin.java @@ -34,6 +34,17 @@ import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Retrieves the authorized configurations associated with a given + * username/password pair using the authentication provider defined in + * guacamole.properties. + * + * All authorized configurations will be stored in the current HttpSession. + * + * Success and failure are logged. + * + * @author Michael Jumper + */ public class BasicLogin extends HttpServlet { private Logger logger = LoggerFactory.getLogger(BasicLogin.class); diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogout.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogout.java index e978a0e5d..df1447d33 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogout.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/BasicLogout.java @@ -24,6 +24,12 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +/** + * Logs out the current user by invalidating the associated HttpSession and + * redirecting the user to the login page. + * + * @author Michael Jumper + */ public class BasicLogout extends HttpServlet { @Override diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/ConfigurationList.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/ConfigurationList.java index fc45474e3..dcbc446e6 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/ConfigurationList.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/ConfigurationList.java @@ -27,13 +27,15 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +/** + * Simple HttpServlet which outputs XML containing a list of all authorized + * configurations for the current user. + * + * @author Michael Jumper + */ public class ConfigurationList extends HttpServlet { - private Logger logger = LoggerFactory.getLogger(ConfigurationList.class); - @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException { diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/GuacamoleClassLoader.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/GuacamoleClassLoader.java index e66526ef5..e48a3dd8d 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/GuacamoleClassLoader.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/GuacamoleClassLoader.java @@ -1,6 +1,42 @@ package net.sourceforge.guacamole.net.basic; +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is guacamole-common. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + import java.io.File; import java.io.FilenameFilter; import java.net.MalformedURLException; @@ -12,24 +48,12 @@ import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.basic.properties.BasicGuacamoleProperties; import net.sourceforge.guacamole.properties.GuacamoleProperties; -/* - * Guacamole - Clientless Remote Desktop - * Copyright (C) 2010 Michael Jumper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . +/** + * A ClassLoader implementation which finds classes within a configurable + * directory. This directory is set within guacamole.properties. + * + * @author Michael Jumper */ - public class GuacamoleClassLoader extends ClassLoader { private URLClassLoader classLoader = null; @@ -106,6 +130,14 @@ public class GuacamoleClassLoader extends ClassLoader { } + /** + * Returns an instance of a GuacamoleClassLoader which finds classes + * within the directory configured in guacamole.properties. + * + * @return An instance of a GuacamoleClassLoader. + * @throws GuacamoleException If no instance could be returned due to an + * error. + */ public static GuacamoleClassLoader getInstance() throws GuacamoleException { // If instance could not be created, rethrow original exception diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/AuthenticationProviderProperty.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/AuthenticationProviderProperty.java index 90e982e32..cb1eb03a7 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/AuthenticationProviderProperty.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/AuthenticationProviderProperty.java @@ -24,6 +24,12 @@ import net.sourceforge.guacamole.net.auth.AuthenticationProvider; import net.sourceforge.guacamole.net.basic.GuacamoleClassLoader; import net.sourceforge.guacamole.properties.GuacamoleProperty; +/** + * A GuacamoleProperty whose value is the name of a class to use to + * authenticate users. This class must implement AuthenticationProvider. + * + * @author Michael Jumper + */ public abstract class AuthenticationProviderProperty implements GuacamoleProperty { @Override diff --git a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/BasicGuacamoleProperties.java b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/BasicGuacamoleProperties.java index cb9430e96..4110b4606 100644 --- a/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/BasicGuacamoleProperties.java +++ b/guacamole/src/main/java/net/sourceforge/guacamole/net/basic/properties/BasicGuacamoleProperties.java @@ -1,8 +1,6 @@ package net.sourceforge.guacamole.net.basic.properties; -import net.sourceforge.guacamole.properties.FileGuacamoleProperty; - /* * Guacamole - Clientless Remote Desktop * Copyright (C) 2010 Michael Jumper @@ -21,17 +19,24 @@ import net.sourceforge.guacamole.properties.FileGuacamoleProperty; * along with this program. If not, see . */ +import net.sourceforge.guacamole.properties.FileGuacamoleProperty; + +/** + * Properties used by the default Guacamole web application. + * + * @author Michael Jumper + */ public class BasicGuacamoleProperties { + /** + * This class should not be instantiated. + */ private BasicGuacamoleProperties() {} - public static final FileGuacamoleProperty BASIC_USER_MAPPING = new FileGuacamoleProperty() { - - @Override - public String getName() { return "basic-user-mapping"; } - - }; - + /** + * The authentication provider to user when retrieving the authorized + * configurations of a user. + */ public static final AuthenticationProviderProperty AUTH_PROVIDER = new AuthenticationProviderProperty() { @Override @@ -39,6 +44,9 @@ public class BasicGuacamoleProperties { }; + /** + * The directory to search for authentication provider classes. + */ public static final FileGuacamoleProperty LIB_DIRECTORY = new FileGuacamoleProperty() { @Override diff --git a/guacamole/src/main/webapp/client.xhtml b/guacamole/src/main/webapp/client.xhtml index be2250951..ee9cd21cf 100644 --- a/guacamole/src/main/webapp/client.xhtml +++ b/guacamole/src/main/webapp/client.xhtml @@ -56,10 +56,14 @@
+ + + +
-
+
diff --git a/guacamole/src/main/webapp/scripts/interface.js b/guacamole/src/main/webapp/scripts/interface.js index c2d5a5d09..03f83ee23 100644 --- a/guacamole/src/main/webapp/scripts/interface.js +++ b/guacamole/src/main/webapp/scripts/interface.js @@ -2,10 +2,11 @@ // UI Definition var GuacamoleUI = { - "display": document.getElementById("display"), - "menu" : document.getElementById("menu"), - "logo" : document.getElementById("status-logo"), - "state" : document.getElementById("state"), + "display" : document.getElementById("display"), + "menu" : document.getElementById("menu"), + "menuControl" : document.getElementById("menuControl"), + "logo" : document.getElementById("status-logo"), + "state" : document.getElementById("state"), "buttons": { @@ -55,7 +56,7 @@ var GuacamoleUI = { if (!menu_shaded) { - var step = Math.floor(GuacamoleUI.menu.offsetHeight / 5) + 1; + var step = Math.floor(GuacamoleUI.menu.offsetHeight / 10) + 1; var offset = 0; menu_shaded = true; @@ -137,13 +138,85 @@ var GuacamoleUI = { window.location.href = "logout"; }; - GuacamoleUI.display.onmouseout = function() { - GuacamoleUI.showMenu(); - }; + // Timeouts for detecting if users wants menu to open or close + var detectMenuOpenTimeout = null; + var detectMenuCloseTimeout = null; - GuacamoleUI.display.onmouseover = function() { - GuacamoleUI.shadeMenu(); - }; + // Clear detection timeouts + function resetMenuDetect() { + + if (detectMenuOpenTimeout != null) { + window.clearTimeout(detectMenuOpenTimeout); + detectMenuOpenTimeout = null; + } + + if (detectMenuCloseTimeout != null) { + window.clearTimeout(detectMenuCloseTimeout); + detectMenuCloseTimeout = null; + } + + } + + // Initiate detection of menu open action. If not canceled through some + // user event, menu will open. + function startMenuOpenDetect() { + + // Clear detection state + resetMenuDetect(); + + // Wait and then show menu + detectMenuOpenTimeout = window.setTimeout(function() { + GuacamoleUI.showMenu(); + detectMenuOpenTimeout = null; + }, 325); + + } + + // Initiate detection of menu close action. If not canceled through some + // user event, menu will close. + function startMenuCloseDetect() { + + // Clear detection state + resetMenuDetect(); + + // Wait and then shade menu + detectMenuCloseTimeout = window.setTimeout(function() { + GuacamoleUI.shadeMenu(); + detectMenuCloseTimeout = null; + }, 500); + + } + + // Show menu if mouseover any part of menu + GuacamoleUI.menu.addEventListener('mouseover', GuacamoleUI.showMenu, true); + + // Stop detecting menu state change intents if mouse is over menu + GuacamoleUI.menu.addEventListener('mouseover', resetMenuDetect, true); + + // When mouse hovers over top of screen, start detection of intent to open menu + GuacamoleUI.menuControl.addEventListener('mousemove', startMenuOpenDetect, true); + + // When mouse enters display, start detection of intent to close menu + GuacamoleUI.display.addEventListener('mouseover', startMenuCloseDetect, true); + + // Show menu if mouse leaves document + document.addEventListener('mouseout', function(e) { + + // Get parent of the element the mouse pointer is leaving + if (!e) e = window.event; + var target = e.relatedTarget || e.toElement; + + // Ensure target is not document nor child of document + var targetParent = target; + while (targetParent != null) { + if (targetParent == document) return; + targetParent = targetParent.parentNode; + } + + // Start detection of intent to open menu + startMenuOpenDetect(); + + }, true); // Reconnect button GuacamoleUI.buttons.reconnect.onclick = function() { @@ -163,10 +236,6 @@ GuacamoleUI.attach = function(guac) { var mouse = new Guacamole.Mouse(GuacamoleUI.display); mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = function(mouseState) { - - if (mouseState.y <= 5) - GuacamoleUI.showMenu(); - guac.sendMouseState(mouseState); }; diff --git a/guacamole/src/main/webapp/styles/client.css b/guacamole/src/main/webapp/styles/client.css index 3dfe2bf6a..f853effa3 100644 --- a/guacamole/src/main/webapp/styles/client.css +++ b/guacamole/src/main/webapp/styles/client.css @@ -73,17 +73,14 @@ div.errorDialog p { #menu { - margin-left: auto; - margin-right: auto; - margin-bottom: 1em; - font-size: 0.8em; - background: #FEA; - border: 1px solid black; position: fixed; left: 0; top: 0; width: 100%; - z-index: 1; + z-index: 4; + background: #FEA; + border-bottom: 1px solid black; + font-size: 0.8em; } #menu.error { @@ -127,6 +124,7 @@ div#clipboardDiv { border: 1px solid black; width: 50em; + z-index: 2; opacity: 0.5; } @@ -161,3 +159,14 @@ div#clipboardDiv textarea { cursor: url('../images/mouse/dot.gif'),url('../images/mouse/blank.cur'),default; } +div#menuControl { + position: fixed; + top: 0; + left: 0; + + width: 100%; + height: 3px; + background: none; + + z-index: 3; +}