+++ /dev/null
-servlet.api.jar=/usr/share/tomcat6/lib/servlet-api.jar
-
-src.dir=src
-web.dir=web
-
-build.dir=build
-dist.dir=dist
-doc.dir=doc
-
-guac.version=0.3.1rc1
-tar.dir=guacamole-${guac.version}
-tar.src.dir=guacamole-src
-
+++ /dev/null
-<?xml version="1.0"?>
-
-<project name="guacamole" default="compile" basedir=".">
-
- <property file="ant/build.properties"/>
-
- <target name="clean" description="Removes build and dist dirs.">
- <delete dir="${build.dir}"/>
- <delete dir="${dist.dir}"/>
- <delete dir="${tar.dir}"/>
- <delete dir="${tar.src.dir}"/>
- <delete file="${tar.dir}.tar.gz"/>
- </target>
-
- <target name="init" description="Creates build and dist dirs.">
- <mkdir dir="${build.dir}"/>
- <mkdir dir="${build.dir}/WEB-INF/classes"/>
- <mkdir dir="${dist.dir}"/>
- </target>
-
- <target name="compile" description="Compiles Guacamole."
- depends="init">
- <copy todir="${build.dir}">
- <fileset dir="${web.dir}"/>
- </copy>
-
- <!-- Replace __GUAC_VERSION with declared version -->
- <replace dir="${build.dir}" value="${guac.version}">
- <include name="**/*.html"/>
- <replacetoken><![CDATA[__GUAC_VERSION]]></replacetoken>
- </replace>
-
- <javac srcdir="${src.dir}" destdir="${build.dir}/WEB-INF/classes"
- classpath="${servlet.api.jar}">
- <compilerarg value="-Xlint:unchecked"/>
- </javac>
- </target>
-
- <target name="war" description="Builds Guacamole .war file."
- depends="compile">
- <war destfile="${dist.dir}/guacamole.war"
- webxml="${build.dir}/WEB-INF/web.xml">
- <fileset dir="${build.dir}"/>
- </war>
- </target>
-
- <target name="tar" description="Build distributable .tar.gz file"
- depends="war">
- <mkdir dir="${tar.dir}"/>
-
- <mkdir dir="${tar.src.dir}"/>
- <copy todir="${tar.src.dir}">
- <fileset dir=".">
- <exclude name=".git/**"/>
- <exclude name="**/.svn"/>
- <exclude name="${tar.dir}/**"/>
- <exclude name="${tar.src.dir}/**"/>
- <exclude name="${dist.dir}/**"/>
- <exclude name="${build.dir}/**"/>
- </fileset>
- </copy>
- <tar basedir="." includes="${tar.src.dir}/**" destfile="${tar.dir}/${tar.src.dir}.tar" longfile="gnu"/>
- <delete dir="${tar.src.dir}"/>
-
- <copy todir="${tar.dir}">
- <fileset file="${doc.dir}/example/guacamole.properties"/>
- <fileset file="${doc.dir}/example/user-mapping.xml"/>
- <fileset file="${dist.dir}/guacamole.war"/>
- <fileset file="LICENSE.txt"/>
- </copy>
-
- <tar basedir="." includes="${tar.dir}/**" destfile="${tar.dir}.tar.gz" compression="gzip" longfile="gnu"/>
- </target>
-
-</project>
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>net.sourceforge.guacamole</groupId>
+ <artifactId>guacamole-common</artifactId>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
+ <name>guacamole-common</name>
+ <url>http://maven.apache.org</url>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+
+package net.sourceforge.guacamole;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import net.sourceforge.guacamole.GuacamoleException;
+
+public abstract class Client {
+
+ public abstract void write(char[] chunk, int off, int len) throws GuacamoleException;
+ public abstract char[] read() throws GuacamoleException;
+ public abstract void disconnect() throws GuacamoleException;
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.InputStreamReader;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+
+import net.sourceforge.guacamole.GuacamoleException;
+
+public class GuacamoleClient extends Client {
+
+ private Socket sock;
+ private Reader input;
+ private Writer output;
+
+ public GuacamoleClient(String hostname, int port) throws GuacamoleException {
+
+ try {
+ sock = new Socket(InetAddress.getByName(hostname), port);
+ input = new InputStreamReader(sock.getInputStream());
+ output = new OutputStreamWriter(sock.getOutputStream());
+ }
+ catch (IOException e) {
+ throw new GuacamoleException(e);
+ }
+
+ }
+
+ public void write(char[] chunk, int off, int len) throws GuacamoleException {
+ try {
+ output.write(chunk, off, len);
+ output.flush();
+ }
+ catch (IOException e) {
+ throw new GuacamoleException(e);
+ }
+ }
+
+ public void disconnect() throws GuacamoleException {
+ try {
+ sock.close();
+ }
+ catch (IOException e) {
+ throw new GuacamoleException(e);
+ }
+ }
+
+ private int usedLength = 0;
+ private char[] buffer = new char[20000];
+
+ public char[] read() throws GuacamoleException {
+
+ try {
+
+ // While we're blocking, or input is available
+ for (;;) {
+
+ // If past threshold, resize buffer before reading
+ if (usedLength > buffer.length/2) {
+ char[] biggerBuffer = new char[buffer.length*2];
+ System.arraycopy(buffer, 0, biggerBuffer, 0, usedLength);
+ buffer = biggerBuffer;
+ }
+
+ // Attempt to fill buffer
+ int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
+ if (numRead == -1)
+ return null;
+
+ int prevLength = usedLength;
+ usedLength += numRead;
+
+ for (int i=usedLength-1; i>=prevLength; i--) {
+
+ char readChar = buffer[i];
+
+ // If end of instruction, return it.
+ if (readChar == ';') {
+
+ // Get instruction
+ char[] chunk = new char[i+1];
+ System.arraycopy(buffer, 0, chunk, 0, i+1);
+
+ // Reset buffer
+ usedLength -= i+1;
+ System.arraycopy(buffer, i+1, buffer, 0, usedLength);
+
+ // Return instruction string
+ return chunk;
+ }
+
+ }
+
+ } // End read loop
+
+ }
+ catch (IOException e) {
+ throw new GuacamoleException(e);
+ }
+
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+public class GuacamoleException extends Exception {
+
+ public GuacamoleException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public GuacamoleException(String message) {
+ super(message);
+ }
+
+ public GuacamoleException(Throwable cause) {
+ super(cause);
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import javax.servlet.ServletContext;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public abstract class Configuration {
+
+ protected String humanReadableList(Object... values) {
+
+ String list = "";
+ for (int i=0; i<values.length; i++) {
+
+ if (i >= 1)
+ list += ", ";
+
+ if (i == values.length -1)
+ list += " or ";
+
+ list += "\"" + values[i] + "\"";
+ }
+
+ return list;
+
+ }
+
+ protected String readParameter(String name) throws GuacamoleException {
+ String value = GuacamoleProperties.getProperty(name);
+ return value;
+ }
+
+ protected String readParameter(String name, String defaultValue, String... allowedValues) throws GuacamoleException {
+
+ String value = GuacamoleProperties.getProperty(name);
+
+ // Use default if not specified
+ if (value == null) {
+ if (defaultValue == null)
+ throw new GuacamoleException("Parameter \"" + name + "\" is required.");
+
+ return defaultValue;
+ }
+
+ // If not restricted to certain values, just return whatever is given.
+ if (allowedValues.length == 0)
+ return value;
+
+ // If restricted, only return value within given list
+ for (String allowedValue : allowedValues)
+ if (value.equals(allowedValue))
+ return value;
+
+ throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
+ }
+
+ protected boolean readBooleanParameter(String name, Boolean defaultValue) throws GuacamoleException {
+
+ String value = GuacamoleProperties.getProperty(name);
+
+ // Use default if not specified
+ if (value == null) {
+ if (defaultValue == null)
+ throw new GuacamoleException("Parameter \"" + name + "\" is required.");
+
+ return defaultValue;
+ }
+
+ value = value.trim();
+ if (value.equals("true"))
+ return true;
+
+ if (value.equals("false"))
+ return false;
+
+ throw new GuacamoleException("Parameter \"" + name + "\" must be \"true\" or \"false\".");
+
+ }
+
+ protected int readIntParameter(String name, Integer defaultValue, Integer... allowedValues) throws GuacamoleException {
+
+ String parmString = GuacamoleProperties.getProperty(name);
+
+ // Use default if not specified
+ if (parmString== null) {
+ if (defaultValue == null)
+ throw new GuacamoleException("Parameter \"" + name + "\" is required.");
+
+ return defaultValue;
+ }
+
+ try {
+ int value = Integer.parseInt(parmString);
+
+ // If not restricted to certain values, just return whatever is given.
+ if (allowedValues.length == 0)
+ return value;
+
+ // If restricted, only return value within given list
+ for (int allowedValue : allowedValues)
+ if (value == allowedValue)
+ return value;
+
+ throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
+ }
+ catch (NumberFormatException e) {
+ throw new GuacamoleException("Parameter \"" + name + "\" must be an integer.", e);
+ }
+
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
+import java.lang.reflect.InvocationTargetException;
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public class GuacamoleConfiguration extends Configuration {
+
+ private String guacd_hostname;
+ private int guacd_port;
+ private GuacamoleSessionProvider sessionProvider;
+
+ public GuacamoleConfiguration() throws GuacamoleException {
+
+ guacd_hostname = readParameter("guacd-hostname");
+ guacd_port = readIntParameter("guacd-port", null);
+
+ // Get session provider instance
+ try {
+ String sessionProviderClassName = readParameter("session-provider");
+ Object obj = Class.forName(sessionProviderClassName).getConstructor().newInstance();
+ if (!(obj instanceof GuacamoleSessionProvider))
+ throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
+
+ sessionProvider = (GuacamoleSessionProvider) obj;
+ }
+ catch (ClassNotFoundException e) {
+ throw new GuacamoleException("Session provider class not found", e);
+ }
+ catch (NoSuchMethodException e) {
+ throw new GuacamoleException("Default constructor for session provider not present", e);
+ }
+ catch (SecurityException e) {
+ throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
+ }
+ catch (InstantiationException e) {
+ throw new GuacamoleException("Unable to instantiate session provider", e);
+ }
+ catch (IllegalAccessException e) {
+ throw new GuacamoleException("Unable to access default constructor of session provider", e);
+ }
+ catch (InvocationTargetException e) {
+ throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
+ }
+
+ }
+
+ public int getProxyPort() {
+ return guacd_port;
+ }
+
+ public String getProxyHostname() {
+ return guacd_hostname;
+ }
+
+ public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
+ return sessionProvider.createSession(session);
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public class GuacamoleProperties {
+
+ private static final Properties properties;
+ private static GuacamoleException exception;
+
+ static {
+
+ properties = new Properties();
+
+ try {
+
+ InputStream stream = GuacamoleProperties.class.getResourceAsStream("/guacamole.properties");
+ if (stream == null)
+ throw new IOException("Resource /guacamole.properties not found.");
+
+ properties.load(stream);
+ }
+ catch (IOException e) {
+ exception = new GuacamoleException("Error reading guacamole.properties", e);
+ }
+
+ }
+
+ public static String getProperty(String name) throws GuacamoleException {
+ if (exception != null) throw exception;
+ return properties.getProperty(name);
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public abstract class GuacamoleServlet extends HttpServlet {
+
+ private GuacamoleConfiguration config;
+
+ @Override
+ public void init() throws ServletException {
+ try {
+ this.config = new GuacamoleConfiguration();
+ }
+ catch (GuacamoleException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ @Override
+ protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ try {
+ handleRequest(req, resp);
+ }
+ catch (GuacamoleException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ @Override
+ protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ try {
+ handleRequest(req, resp);
+ }
+ catch (GuacamoleException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ private final void handleRequest(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ HttpSession httpSession = request.getSession(shouldCreateSession());
+
+ if (httpSession != null) {
+ GuacamoleSession session = config.createSession(httpSession);
+ handleRequest(session, request, response);
+ }
+ else
+ throw new GuacamoleException("No session");
+ }
+
+ protected abstract void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException;
+
+ protected boolean shouldCreateSession() {
+ return false;
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.util.concurrent.locks.ReentrantLock;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import net.sourceforge.guacamole.Client;
+import net.sourceforge.guacamole.GuacamoleClient;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public class GuacamoleSession {
+
+ private GuacamoleConfiguration config;
+ private final HttpSession session;
+ private SessionClient client;
+ private ReentrantLock instructionStreamLock;
+
+ private String protocol;
+ private String hostname;
+ private int port;
+ private String password;
+
+ public class SessionClient extends Client implements HttpSessionBindingListener {
+
+ private Client client;
+ private ReentrantLock authorizedLock;
+
+ public SessionClient(Client client) {
+ this.client = client;
+
+ authorizedLock = new ReentrantLock();
+ authorizedLock.lock();
+ }
+
+ public void authorize() {
+ authorizedLock.unlock();
+ }
+
+ public void waitForAuthorization() {
+ if (authorizedLock.isLocked()) {
+ try {
+ authorizedLock.lock();
+ authorizedLock.unlock();
+ }
+ catch (Throwable t) {
+ throw new Error("Internal error waiting for authorization", t);
+ }
+ }
+ }
+
+ public void valueBound(HttpSessionBindingEvent event) {
+ // Do nothing
+ }
+
+ public void valueUnbound(HttpSessionBindingEvent event) {
+ try {
+ disconnect();
+ }
+ catch (GuacamoleException e) {
+ // Ignore
+ }
+ }
+
+ public void write(char[] data, int off, int len) throws GuacamoleException {
+ client.write(data, off, len);
+ }
+
+ public char[] read() throws GuacamoleException {
+ return client.read();
+ }
+
+ public void disconnect() throws GuacamoleException {
+ client.disconnect();
+ }
+
+ }
+
+ public GuacamoleSession(HttpSession session) throws GuacamoleException {
+
+ if (session == null)
+ throw new GuacamoleException("User has no session.");
+
+ this.session = session;
+ synchronized (session) {
+
+ // Read configuration parameters
+ config = new GuacamoleConfiguration();
+
+ client = (SessionClient) session.getAttribute("CLIENT");
+ instructionStreamLock = (ReentrantLock) session.getAttribute("INSTRUCTION_STREAM_LOCK");
+ }
+ }
+
+ public void connect() throws GuacamoleException {
+ synchronized (session) {
+
+ if (client != null)
+ client.disconnect();
+
+
+ client = new SessionClient(
+ new GuacamoleClient (
+ config.getProxyHostname(),
+ config.getProxyPort()
+ )
+ );
+
+ session.setAttribute("CLIENT", client);
+
+ instructionStreamLock = new ReentrantLock();
+ session.setAttribute("INSTRUCTION_STREAM_LOCK", instructionStreamLock);
+
+ }
+ }
+
+ public boolean isConnected() {
+ synchronized (session) {
+ return client != null;
+ }
+ }
+
+ public GuacamoleConfiguration getConfiguration() {
+ return config;
+ }
+
+ public SessionClient getClient() {
+ synchronized (session) {
+ return client;
+ }
+ }
+
+ public void invalidate() {
+ session.invalidate();
+ }
+
+ public void disconnect() throws GuacamoleException {
+ if (client != null) {
+ client.disconnect();
+
+ session.removeAttribute("CLIENT");
+ client = null;
+ }
+ }
+
+ public ReentrantLock getInstructionStreamLock() {
+ return instructionStreamLock;
+ }
+
+ public void setConnection(String protocol, String hostname, int port) {
+ this.protocol = protocol;
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getConnectMessage() throws GuacamoleException {
+
+ if (getProtocol() == null)
+ throw new GuacamoleException("Protocol not specified");
+
+ if (getHostname() == null)
+ throw new GuacamoleException("Hostname not specified");
+
+ if (getPassword() == null)
+ return "connect:" + getProtocol() + "," + getHostname() + "," + getPort() + ";";
+ else
+ return "connect:" + getProtocol() + "," + getHostname() + "," + getPort() + "," + getPassword() + ";";
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net.authentication;
+
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.GuacamoleSession;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+public interface GuacamoleSessionProvider {
+
+ public GuacamoleSession createSession(HttpSession session) throws GuacamoleException;
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net.authentication;
+
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.GuacamoleSession;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+public class NullGuacamoleSessionProvider implements GuacamoleSessionProvider {
+
+ public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
+ throw new GuacamoleException("Null provider will not create sessions");
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net.authentication.basic;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.GuacamoleProperties;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+public class BasicFileAuthenticationProvider implements BasicLogin.AuthenticationProvider {
+
+ private long mappingTime;
+ private Map<String, AuthInfo> mapping;
+
+ private File getUserMappingFile() throws GuacamoleException {
+
+ // Get user mapping filename
+ String filename = GuacamoleProperties.getProperty("basic-user-mapping");
+ if (filename == null)
+ return null;
+
+ return new File(filename);
+
+ }
+
+ public synchronized void init() throws GuacamoleException {
+
+ // Get user mapping file
+ File mapFile = getUserMappingFile();
+ if (mapFile == null)
+ throw new GuacamoleException("Missing \"basic-user-mapping\" parameter required for basic login.");
+
+ // Parse document
+ try {
+
+ BasicUserMappingContentHandler contentHandler = new BasicUserMappingContentHandler();
+
+ XMLReader parser = XMLReaderFactory.createXMLReader();
+ parser.setContentHandler(contentHandler);
+ parser.parse(mapFile.getAbsolutePath());
+
+ mappingTime = mapFile.lastModified();
+ mapping = contentHandler.getUserMapping();
+
+ }
+ catch (IOException e) {
+ throw new GuacamoleException("Error reading basic user mapping file.", e);
+ }
+ catch (SAXException e) {
+ throw new GuacamoleException("Error parsing basic user mapping XML.", e);
+ }
+
+ }
+
+ @Override
+ public BasicLogin.AuthorizedConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException {
+
+ // Check mapping file mod time
+ File userMappingFile = getUserMappingFile();
+ if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified()) {
+
+ // If modified recently, gain exclusive access and recheck
+ synchronized (this) {
+ if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified())
+ init(); // If still not up to date, re-init
+ }
+
+ }
+
+ AuthInfo info = mapping.get(username);
+ if (info != null && info.validate(username, password))
+ return new BasicLogin.AuthorizedConfiguration(
+ info.getProtocol(),
+ info.getHostname(),
+ info.getPort(),
+ info.getPassword()
+ );
+
+ return null;
+
+ }
+
+ public static class AuthInfo {
+
+ public static enum Encoding {
+ PLAIN_TEXT,
+ MD5
+ }
+
+ private String auth_username;
+ private String auth_password;
+ private Encoding auth_encoding;
+
+ private String protocol;
+ private String hostname;
+ private int port;
+ private String password;
+
+ public AuthInfo(String auth_username, String auth_password, Encoding auth_encoding) {
+ this.auth_username = auth_username;
+ this.auth_password = auth_password;
+ this.auth_encoding = auth_encoding;
+ }
+
+ private static final char HEX_CHARS[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ public static String getHexString(byte[] bytes) {
+
+ if (bytes == null)
+ return null;
+
+ StringBuilder hex = new StringBuilder(2 * bytes.length);
+ for (byte b : bytes) {
+ hex.append(HEX_CHARS[(b & 0xF0) >> 4])
+ .append(HEX_CHARS[(b & 0x0F) ]);
+ }
+
+ return hex.toString();
+
+ }
+
+
+ public boolean validate(String username, String password) {
+
+ // If username matches
+ if (username != null && password != null && username.equals(auth_username)) {
+
+ switch (auth_encoding) {
+
+ case PLAIN_TEXT:
+
+ // Compare plaintext
+ return password.equals(auth_password);
+
+ case MD5:
+
+ // Compare hashed password
+ try {
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ String hashedPassword = getHexString(digest.digest(password.getBytes()));
+ return hashedPassword.equals(auth_password.toUpperCase());
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedOperationException("Unexpected lack of MD5 support.", e);
+ }
+
+ }
+
+ }
+
+ return false;
+
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ }
+
+
+ private static class BasicUserMappingContentHandler extends DefaultHandler {
+
+ private Map<String, AuthInfo> authMapping = new HashMap<String, AuthInfo>();
+
+ public Map<String, AuthInfo> getUserMapping() {
+ return Collections.unmodifiableMap(authMapping);
+ }
+
+ private AuthInfo current;
+
+ private enum AUTH_INFO_STATE {
+ PROTOCOL,
+ HOSTNAME,
+ PORT,
+ PASSWORD
+ };
+
+ private AUTH_INFO_STATE infoState;
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+
+ if (localName.equals("authorize")) {
+
+ // Finalize mapping for this user
+ authMapping.put(
+ current.auth_username,
+ current
+ );
+
+ }
+
+ infoState = null;
+
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+
+ if (localName.equals("authorize")) {
+
+ AuthInfo.Encoding encoding;
+ String encodingString = attributes.getValue("encoding");
+ if (encodingString == null)
+ encoding = AuthInfo.Encoding.PLAIN_TEXT;
+ else if (encodingString.equals("plain"))
+ encoding = AuthInfo.Encoding.PLAIN_TEXT;
+ else if (encodingString.equals("md5"))
+ encoding = AuthInfo.Encoding.MD5;
+ else
+ throw new SAXException("Invalid encoding type");
+
+
+ current = new AuthInfo(
+ attributes.getValue("username"),
+ attributes.getValue("password"),
+ encoding
+ );
+
+ infoState = null;
+
+ }
+
+ else if (localName.equals("protocol"))
+ infoState = AUTH_INFO_STATE.PROTOCOL;
+
+ else if (localName.equals("hostname"))
+ infoState = AUTH_INFO_STATE.HOSTNAME;
+
+ else if (localName.equals("port"))
+ infoState = AUTH_INFO_STATE.PORT;
+
+ else if (localName.equals("password"))
+ infoState = AUTH_INFO_STATE.PASSWORD;
+
+ else
+ infoState = null;
+
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+
+ String str = new String(ch, start, length);
+
+ if (infoState == null)
+ return;
+
+ switch (infoState) {
+
+ case PROTOCOL:
+ current.protocol = str;
+ break;
+
+ case HOSTNAME:
+ current.hostname = str;
+ break;
+
+ case PORT:
+ current.port = Integer.parseInt(str);
+ break;
+
+ case PASSWORD:
+ current.password = str;
+ break;
+
+ }
+
+ }
+
+
+ }
+
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net.authentication.basic;
+
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.GuacamoleSession;
+import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+public class BasicGuacamoleSessionProvider implements GuacamoleSessionProvider {
+
+ public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
+
+ // Retrieve authorized config data from session
+ BasicLogin.AuthorizedConfiguration config = (BasicLogin.AuthorizedConfiguration)
+ session.getAttribute("BASIC-LOGIN-AUTH");
+
+ // If no data, not authorized
+ if (config == null)
+ throw new GuacamoleException("Unauthorized");
+
+ // Configure session from authorized config info
+ GuacamoleSession guacSession = new GuacamoleSession(session);
+ guacSession.setConnection(config.getProtocol(), config.getHostname(), config.getPort());
+ if (config.getPassword() != null)
+ guacSession.setPassword(config.getPassword());
+
+ // Return authorized session
+ return guacSession;
+
+ }
+
+}
--- /dev/null
+
+package net.sourceforge.guacamole.net.authentication.basic;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.Configuration;
+
+public class BasicLogin extends HttpServlet {
+
+ private Config config;
+
+ @Override
+ public void init() throws ServletException {
+ try {
+ config = new Config();
+ }
+ catch (GuacamoleException e) {
+ throw new ServletException(e);
+ }
+ }
+
+
+ private class Config extends Configuration {
+
+ private AuthenticationProvider authProvider;
+
+ public Config() throws GuacamoleException {
+
+ // Get auth provider instance
+ try {
+ String authProviderClassName = readParameter("auth-provider");
+ Object obj = Class.forName(authProviderClassName).getConstructor().newInstance();
+ if (!(obj instanceof AuthenticationProvider))
+ throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
+
+ authProvider = (AuthenticationProvider) obj;
+ }
+ catch (ClassNotFoundException e) {
+ throw new GuacamoleException("Session provider class not found", e);
+ }
+ catch (NoSuchMethodException e) {
+ throw new GuacamoleException("Default constructor for session provider not present", e);
+ }
+ catch (SecurityException e) {
+ throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
+ }
+ catch (InstantiationException e) {
+ throw new GuacamoleException("Unable to instantiate session provider", e);
+ }
+ catch (IllegalAccessException e) {
+ throw new GuacamoleException("Unable to access default constructor of session provider", e);
+ }
+ catch (InvocationTargetException e) {
+ throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
+ }
+
+ }
+
+ public AuthenticationProvider getAuthenticationProvider() {
+ return authProvider;
+ }
+
+ }
+
+ public static interface AuthenticationProvider {
+ public AuthorizedConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException;
+ }
+
+ // Added to session when session validated
+ public static class AuthorizedConfiguration {
+
+ private String protocol;
+ private String hostname;
+ private int port;
+ private String password;
+
+ public AuthorizedConfiguration(String protocol, String hostname, int port, String password) {
+ this.protocol = protocol;
+ this.hostname = hostname;
+ this.port = port;
+ this.password = password;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+ // Retrieve username and password from parms
+ String username = req.getParameter("username");
+ String password = req.getParameter("password");
+
+ // Validate username and password
+ try {
+
+ AuthorizedConfiguration info = config.getAuthenticationProvider().getAuthorizedConfiguration(username, password);
+ if (info != null) {
+
+ // Store authorized configuration
+ HttpSession session = req.getSession(true);
+ session.setAttribute(
+ "BASIC-LOGIN-AUTH",
+ info
+ );
+
+ // Success
+ return;
+
+ }
+
+ // Report "forbidden" on any failure
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
+ }
+ catch (GuacamoleException e) {
+ throw new ServletException("Error validating credentials", e);
+ }
+
+ }
+
+
+}
--- /dev/null
+package net.sourceforge.guacamole.net.tunnel;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import net.sourceforge.guacamole.GuacamoleException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.sourceforge.guacamole.net.GuacamoleServlet;
+
+import net.sourceforge.guacamole.net.GuacamoleSession;
+
+public class Connect extends GuacamoleServlet {
+
+ @Override
+ protected boolean shouldCreateSession() {
+ return true;
+ }
+
+ @Override
+ protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ // Disconnect if already connected
+ if (session.isConnected())
+ session.disconnect();
+
+ // Obtain new connection
+ session.connect();
+
+ // Send data
+ try {
+ char[] connect = session.getConnectMessage().toCharArray();
+ session.getClient().write(connect, 0, connect.length);
+ session.getClient().authorize();
+ }
+ catch (GuacamoleException e) {
+ throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e);
+ }
+
+ }
+
+}
+
--- /dev/null
+package net.sourceforge.guacamole.net.tunnel;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import net.sourceforge.guacamole.GuacamoleException;
+
+import java.io.Reader;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.sourceforge.guacamole.net.GuacamoleServlet;
+
+import net.sourceforge.guacamole.net.GuacamoleSession;
+
+public class Inbound extends GuacamoleServlet {
+
+ @Override
+ protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ session.getClient().waitForAuthorization();
+
+ // Send data
+ try {
+
+ Reader input = request.getReader();
+ char[] buffer = new char[8192];
+
+ int length;
+ while ((length = input.read(buffer, 0, buffer.length)) != -1)
+ session.getClient().write(buffer, 0, length);
+
+ }
+ catch (IOException e) {
+ throw new GuacamoleException("I/O Error sending data to server: " + e.getMessage(), e);
+ }
+ catch (GuacamoleException e) {
+ throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e);
+ }
+
+ }
+
+}
+
--- /dev/null
+package net.sourceforge.guacamole.net.tunnel;
+
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+import java.io.Writer;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.sourceforge.guacamole.Client;
+import net.sourceforge.guacamole.net.GuacamoleServlet;
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.net.GuacamoleSession;
+
+
+public class Outbound extends GuacamoleServlet {
+
+ @Override
+ protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ session.getClient().waitForAuthorization();
+
+ ReentrantLock instructionStreamLock = session.getInstructionStreamLock();
+ instructionStreamLock.lock();
+
+ try {
+
+ response.setContentType("text/plain");
+ Writer out = response.getWriter();
+
+ try {
+
+ // Query new update from server
+ Client client = session.getClient();
+
+ // For all messages, until another stream is ready (we send at least one message)
+ char[] message;
+ while ((message = client.read()) != null) {
+
+ // Get message output bytes
+ out.write(message, 0, message.length);
+ out.flush();
+ response.flushBuffer();
+
+ // No more messages another stream can take over
+ if (instructionStreamLock.hasQueuedThreads())
+ break;
+
+ }
+
+ if (message == null) {
+ session.disconnect();
+ throw new GuacamoleException("Disconnected.");
+ }
+
+ }
+ catch (GuacamoleException e) {
+ out.write("error:" + e.getMessage() + ";");
+ out.flush();
+ response.flushBuffer();
+ }
+
+ // End-of-instructions marker
+ out.write(';');
+ out.flush();
+ response.flushBuffer();
+
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new GuacamoleException("UTF-8 not supported by Java.", e);
+ }
+ catch (IOException e) {
+ throw new GuacamoleException("I/O error writing to servlet output stream.", e);
+ }
+ finally {
+ instructionStreamLock.unlock();
+ }
+
+ }
+
+}
+
+++ /dev/null
-
-package net.sourceforge.guacamole;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import net.sourceforge.guacamole.GuacamoleException;
-
-public abstract class Client {
-
- public abstract void write(char[] chunk, int off, int len) throws GuacamoleException;
- public abstract char[] read() throws GuacamoleException;
- public abstract void disconnect() throws GuacamoleException;
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.InputStreamReader;
-
-import java.io.OutputStream;
-import java.io.Writer;
-import java.io.OutputStreamWriter;
-
-import net.sourceforge.guacamole.GuacamoleException;
-
-public class GuacamoleClient extends Client {
-
- private Socket sock;
- private Reader input;
- private Writer output;
-
- public GuacamoleClient(String hostname, int port) throws GuacamoleException {
-
- try {
- sock = new Socket(InetAddress.getByName(hostname), port);
- input = new InputStreamReader(sock.getInputStream());
- output = new OutputStreamWriter(sock.getOutputStream());
- }
- catch (IOException e) {
- throw new GuacamoleException(e);
- }
-
- }
-
- public void write(char[] chunk, int off, int len) throws GuacamoleException {
- try {
- output.write(chunk, off, len);
- output.flush();
- }
- catch (IOException e) {
- throw new GuacamoleException(e);
- }
- }
-
- public void disconnect() throws GuacamoleException {
- try {
- sock.close();
- }
- catch (IOException e) {
- throw new GuacamoleException(e);
- }
- }
-
- private int usedLength = 0;
- private char[] buffer = new char[20000];
-
- public char[] read() throws GuacamoleException {
-
- try {
-
- // While we're blocking, or input is available
- for (;;) {
-
- // If past threshold, resize buffer before reading
- if (usedLength > buffer.length/2) {
- char[] biggerBuffer = new char[buffer.length*2];
- System.arraycopy(buffer, 0, biggerBuffer, 0, usedLength);
- buffer = biggerBuffer;
- }
-
- // Attempt to fill buffer
- int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
- if (numRead == -1)
- return null;
-
- int prevLength = usedLength;
- usedLength += numRead;
-
- for (int i=usedLength-1; i>=prevLength; i--) {
-
- char readChar = buffer[i];
-
- // If end of instruction, return it.
- if (readChar == ';') {
-
- // Get instruction
- char[] chunk = new char[i+1];
- System.arraycopy(buffer, 0, chunk, 0, i+1);
-
- // Reset buffer
- usedLength -= i+1;
- System.arraycopy(buffer, i+1, buffer, 0, usedLength);
-
- // Return instruction string
- return chunk;
- }
-
- }
-
- } // End read loop
-
- }
- catch (IOException e) {
- throw new GuacamoleException(e);
- }
-
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-public class GuacamoleException extends Exception {
-
- public GuacamoleException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public GuacamoleException(String message) {
- super(message);
- }
-
- public GuacamoleException(Throwable cause) {
- super(cause);
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import javax.servlet.ServletContext;
-import net.sourceforge.guacamole.GuacamoleException;
-
-public abstract class Configuration {
-
- protected String humanReadableList(Object... values) {
-
- String list = "";
- for (int i=0; i<values.length; i++) {
-
- if (i >= 1)
- list += ", ";
-
- if (i == values.length -1)
- list += " or ";
-
- list += "\"" + values[i] + "\"";
- }
-
- return list;
-
- }
-
- protected String readParameter(String name) throws GuacamoleException {
- String value = GuacamoleProperties.getProperty(name);
- return value;
- }
-
- protected String readParameter(String name, String defaultValue, String... allowedValues) throws GuacamoleException {
-
- String value = GuacamoleProperties.getProperty(name);
-
- // Use default if not specified
- if (value == null) {
- if (defaultValue == null)
- throw new GuacamoleException("Parameter \"" + name + "\" is required.");
-
- return defaultValue;
- }
-
- // If not restricted to certain values, just return whatever is given.
- if (allowedValues.length == 0)
- return value;
-
- // If restricted, only return value within given list
- for (String allowedValue : allowedValues)
- if (value.equals(allowedValue))
- return value;
-
- throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
- }
-
- protected boolean readBooleanParameter(String name, Boolean defaultValue) throws GuacamoleException {
-
- String value = GuacamoleProperties.getProperty(name);
-
- // Use default if not specified
- if (value == null) {
- if (defaultValue == null)
- throw new GuacamoleException("Parameter \"" + name + "\" is required.");
-
- return defaultValue;
- }
-
- value = value.trim();
- if (value.equals("true"))
- return true;
-
- if (value.equals("false"))
- return false;
-
- throw new GuacamoleException("Parameter \"" + name + "\" must be \"true\" or \"false\".");
-
- }
-
- protected int readIntParameter(String name, Integer defaultValue, Integer... allowedValues) throws GuacamoleException {
-
- String parmString = GuacamoleProperties.getProperty(name);
-
- // Use default if not specified
- if (parmString== null) {
- if (defaultValue == null)
- throw new GuacamoleException("Parameter \"" + name + "\" is required.");
-
- return defaultValue;
- }
-
- try {
- int value = Integer.parseInt(parmString);
-
- // If not restricted to certain values, just return whatever is given.
- if (allowedValues.length == 0)
- return value;
-
- // If restricted, only return value within given list
- for (int allowedValue : allowedValues)
- if (value == allowedValue)
- return value;
-
- throw new GuacamoleException("Parameter \"" + name + "\" must be " + humanReadableList((Object) allowedValues));
- }
- catch (NumberFormatException e) {
- throw new GuacamoleException("Parameter \"" + name + "\" must be an integer.", e);
- }
-
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
-import java.lang.reflect.InvocationTargetException;
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-
-public class GuacamoleConfiguration extends Configuration {
-
- private String guacd_hostname;
- private int guacd_port;
- private GuacamoleSessionProvider sessionProvider;
-
- public GuacamoleConfiguration() throws GuacamoleException {
-
- guacd_hostname = readParameter("guacd-hostname");
- guacd_port = readIntParameter("guacd-port", null);
-
- // Get session provider instance
- try {
- String sessionProviderClassName = readParameter("session-provider");
- Object obj = Class.forName(sessionProviderClassName).getConstructor().newInstance();
- if (!(obj instanceof GuacamoleSessionProvider))
- throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
-
- sessionProvider = (GuacamoleSessionProvider) obj;
- }
- catch (ClassNotFoundException e) {
- throw new GuacamoleException("Session provider class not found", e);
- }
- catch (NoSuchMethodException e) {
- throw new GuacamoleException("Default constructor for session provider not present", e);
- }
- catch (SecurityException e) {
- throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
- }
- catch (InstantiationException e) {
- throw new GuacamoleException("Unable to instantiate session provider", e);
- }
- catch (IllegalAccessException e) {
- throw new GuacamoleException("Unable to access default constructor of session provider", e);
- }
- catch (InvocationTargetException e) {
- throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
- }
-
- }
-
- public int getProxyPort() {
- return guacd_port;
- }
-
- public String getProxyHostname() {
- return guacd_hostname;
- }
-
- public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
- return sessionProvider.createSession(session);
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-import net.sourceforge.guacamole.GuacamoleException;
-
-public class GuacamoleProperties {
-
- private static final Properties properties;
- private static GuacamoleException exception;
-
- static {
-
- properties = new Properties();
-
- try {
-
- InputStream stream = GuacamoleProperties.class.getResourceAsStream("/guacamole.properties");
- if (stream == null)
- throw new IOException("Resource /guacamole.properties not found.");
-
- properties.load(stream);
- }
- catch (IOException e) {
- exception = new GuacamoleException("Error reading guacamole.properties", e);
- }
-
- }
-
- public static String getProperty(String name) throws GuacamoleException {
- if (exception != null) throw exception;
- return properties.getProperty(name);
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.IOException;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-
-public abstract class GuacamoleServlet extends HttpServlet {
-
- private GuacamoleConfiguration config;
-
- @Override
- public void init() throws ServletException {
- try {
- this.config = new GuacamoleConfiguration();
- }
- catch (GuacamoleException e) {
- throw new ServletException(e);
- }
- }
-
- @Override
- protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- try {
- handleRequest(req, resp);
- }
- catch (GuacamoleException e) {
- throw new ServletException(e);
- }
- }
-
- @Override
- protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- try {
- handleRequest(req, resp);
- }
- catch (GuacamoleException e) {
- throw new ServletException(e);
- }
- }
-
- private final void handleRequest(HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
-
- HttpSession httpSession = request.getSession(shouldCreateSession());
-
- if (httpSession != null) {
- GuacamoleSession session = config.createSession(httpSession);
- handleRequest(session, request, response);
- }
- else
- throw new GuacamoleException("No session");
- }
-
- protected abstract void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException;
-
- protected boolean shouldCreateSession() {
- return false;
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.util.concurrent.locks.ReentrantLock;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
-import net.sourceforge.guacamole.Client;
-import net.sourceforge.guacamole.GuacamoleClient;
-import net.sourceforge.guacamole.GuacamoleException;
-
-public class GuacamoleSession {
-
- private GuacamoleConfiguration config;
- private final HttpSession session;
- private SessionClient client;
- private ReentrantLock instructionStreamLock;
-
- private String protocol;
- private String hostname;
- private int port;
- private String password;
-
- public class SessionClient extends Client implements HttpSessionBindingListener {
-
- private Client client;
- private ReentrantLock authorizedLock;
-
- public SessionClient(Client client) {
- this.client = client;
-
- authorizedLock = new ReentrantLock();
- authorizedLock.lock();
- }
-
- public void authorize() {
- authorizedLock.unlock();
- }
-
- public void waitForAuthorization() {
- if (authorizedLock.isLocked()) {
- try {
- authorizedLock.lock();
- authorizedLock.unlock();
- }
- catch (Throwable t) {
- throw new Error("Internal error waiting for authorization", t);
- }
- }
- }
-
- public void valueBound(HttpSessionBindingEvent event) {
- // Do nothing
- }
-
- public void valueUnbound(HttpSessionBindingEvent event) {
- try {
- disconnect();
- }
- catch (GuacamoleException e) {
- // Ignore
- }
- }
-
- public void write(char[] data, int off, int len) throws GuacamoleException {
- client.write(data, off, len);
- }
-
- public char[] read() throws GuacamoleException {
- return client.read();
- }
-
- public void disconnect() throws GuacamoleException {
- client.disconnect();
- }
-
- }
-
- public GuacamoleSession(HttpSession session) throws GuacamoleException {
-
- if (session == null)
- throw new GuacamoleException("User has no session.");
-
- this.session = session;
- synchronized (session) {
-
- // Read configuration parameters
- config = new GuacamoleConfiguration();
-
- client = (SessionClient) session.getAttribute("CLIENT");
- instructionStreamLock = (ReentrantLock) session.getAttribute("INSTRUCTION_STREAM_LOCK");
- }
- }
-
- public void connect() throws GuacamoleException {
- synchronized (session) {
-
- if (client != null)
- client.disconnect();
-
-
- client = new SessionClient(
- new GuacamoleClient (
- config.getProxyHostname(),
- config.getProxyPort()
- )
- );
-
- session.setAttribute("CLIENT", client);
-
- instructionStreamLock = new ReentrantLock();
- session.setAttribute("INSTRUCTION_STREAM_LOCK", instructionStreamLock);
-
- }
- }
-
- public boolean isConnected() {
- synchronized (session) {
- return client != null;
- }
- }
-
- public GuacamoleConfiguration getConfiguration() {
- return config;
- }
-
- public SessionClient getClient() {
- synchronized (session) {
- return client;
- }
- }
-
- public void invalidate() {
- session.invalidate();
- }
-
- public void disconnect() throws GuacamoleException {
- if (client != null) {
- client.disconnect();
-
- session.removeAttribute("CLIENT");
- client = null;
- }
- }
-
- public ReentrantLock getInstructionStreamLock() {
- return instructionStreamLock;
- }
-
- public void setConnection(String protocol, String hostname, int port) {
- this.protocol = protocol;
- this.hostname = hostname;
- this.port = port;
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- public String getHostname() {
- return hostname;
- }
-
- public int getPort() {
- return port;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public String getConnectMessage() throws GuacamoleException {
-
- if (getProtocol() == null)
- throw new GuacamoleException("Protocol not specified");
-
- if (getHostname() == null)
- throw new GuacamoleException("Hostname not specified");
-
- if (getPassword() == null)
- return "connect:" + getProtocol() + "," + getHostname() + "," + getPort() + ";";
- else
- return "connect:" + getProtocol() + "," + getHostname() + "," + getPort() + "," + getPassword() + ";";
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net.authentication;
-
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.GuacamoleSession;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-public interface GuacamoleSessionProvider {
-
- public GuacamoleSession createSession(HttpSession session) throws GuacamoleException;
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net.authentication;
-
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.GuacamoleSession;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-public class NullGuacamoleSessionProvider implements GuacamoleSessionProvider {
-
- public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
- throw new GuacamoleException("Null provider will not create sessions");
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.File;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.GuacamoleProperties;
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-public class BasicFileAuthenticationProvider implements BasicLogin.AuthenticationProvider {
-
- private long mappingTime;
- private Map<String, AuthInfo> mapping;
-
- private File getUserMappingFile() throws GuacamoleException {
-
- // Get user mapping filename
- String filename = GuacamoleProperties.getProperty("basic-user-mapping");
- if (filename == null)
- return null;
-
- return new File(filename);
-
- }
-
- public synchronized void init() throws GuacamoleException {
-
- // Get user mapping file
- File mapFile = getUserMappingFile();
- if (mapFile == null)
- throw new GuacamoleException("Missing \"basic-user-mapping\" parameter required for basic login.");
-
- // Parse document
- try {
-
- BasicUserMappingContentHandler contentHandler = new BasicUserMappingContentHandler();
-
- XMLReader parser = XMLReaderFactory.createXMLReader();
- parser.setContentHandler(contentHandler);
- parser.parse(mapFile.getAbsolutePath());
-
- mappingTime = mapFile.lastModified();
- mapping = contentHandler.getUserMapping();
-
- }
- catch (IOException e) {
- throw new GuacamoleException("Error reading basic user mapping file.", e);
- }
- catch (SAXException e) {
- throw new GuacamoleException("Error parsing basic user mapping XML.", e);
- }
-
- }
-
- @Override
- public BasicLogin.AuthorizedConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException {
-
- // Check mapping file mod time
- File userMappingFile = getUserMappingFile();
- if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified()) {
-
- // If modified recently, gain exclusive access and recheck
- synchronized (this) {
- if (userMappingFile.exists() && mappingTime < userMappingFile.lastModified())
- init(); // If still not up to date, re-init
- }
-
- }
-
- AuthInfo info = mapping.get(username);
- if (info != null && info.validate(username, password))
- return new BasicLogin.AuthorizedConfiguration(
- info.getProtocol(),
- info.getHostname(),
- info.getPort(),
- info.getPassword()
- );
-
- return null;
-
- }
-
- public static class AuthInfo {
-
- public static enum Encoding {
- PLAIN_TEXT,
- MD5
- }
-
- private String auth_username;
- private String auth_password;
- private Encoding auth_encoding;
-
- private String protocol;
- private String hostname;
- private int port;
- private String password;
-
- public AuthInfo(String auth_username, String auth_password, Encoding auth_encoding) {
- this.auth_username = auth_username;
- this.auth_password = auth_password;
- this.auth_encoding = auth_encoding;
- }
-
- private static final char HEX_CHARS[] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
- };
-
- public static String getHexString(byte[] bytes) {
-
- if (bytes == null)
- return null;
-
- StringBuilder hex = new StringBuilder(2 * bytes.length);
- for (byte b : bytes) {
- hex.append(HEX_CHARS[(b & 0xF0) >> 4])
- .append(HEX_CHARS[(b & 0x0F) ]);
- }
-
- return hex.toString();
-
- }
-
-
- public boolean validate(String username, String password) {
-
- // If username matches
- if (username != null && password != null && username.equals(auth_username)) {
-
- switch (auth_encoding) {
-
- case PLAIN_TEXT:
-
- // Compare plaintext
- return password.equals(auth_password);
-
- case MD5:
-
- // Compare hashed password
- try {
- MessageDigest digest = MessageDigest.getInstance("MD5");
- String hashedPassword = getHexString(digest.digest(password.getBytes()));
- return hashedPassword.equals(auth_password.toUpperCase());
- }
- catch (NoSuchAlgorithmException e) {
- throw new UnsupportedOperationException("Unexpected lack of MD5 support.", e);
- }
-
- }
-
- }
-
- return false;
-
- }
-
- public String getHostname() {
- return hostname;
- }
-
- public String getPassword() {
- return password;
- }
-
- public int getPort() {
- return port;
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- }
-
-
- private static class BasicUserMappingContentHandler extends DefaultHandler {
-
- private Map<String, AuthInfo> authMapping = new HashMap<String, AuthInfo>();
-
- public Map<String, AuthInfo> getUserMapping() {
- return Collections.unmodifiableMap(authMapping);
- }
-
- private AuthInfo current;
-
- private enum AUTH_INFO_STATE {
- PROTOCOL,
- HOSTNAME,
- PORT,
- PASSWORD
- };
-
- private AUTH_INFO_STATE infoState;
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
-
- if (localName.equals("authorize")) {
-
- // Finalize mapping for this user
- authMapping.put(
- current.auth_username,
- current
- );
-
- }
-
- infoState = null;
-
- }
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
-
- if (localName.equals("authorize")) {
-
- AuthInfo.Encoding encoding;
- String encodingString = attributes.getValue("encoding");
- if (encodingString == null)
- encoding = AuthInfo.Encoding.PLAIN_TEXT;
- else if (encodingString.equals("plain"))
- encoding = AuthInfo.Encoding.PLAIN_TEXT;
- else if (encodingString.equals("md5"))
- encoding = AuthInfo.Encoding.MD5;
- else
- throw new SAXException("Invalid encoding type");
-
-
- current = new AuthInfo(
- attributes.getValue("username"),
- attributes.getValue("password"),
- encoding
- );
-
- infoState = null;
-
- }
-
- else if (localName.equals("protocol"))
- infoState = AUTH_INFO_STATE.PROTOCOL;
-
- else if (localName.equals("hostname"))
- infoState = AUTH_INFO_STATE.HOSTNAME;
-
- else if (localName.equals("port"))
- infoState = AUTH_INFO_STATE.PORT;
-
- else if (localName.equals("password"))
- infoState = AUTH_INFO_STATE.PASSWORD;
-
- else
- infoState = null;
-
- }
-
- @Override
- public void characters(char[] ch, int start, int length) throws SAXException {
-
- String str = new String(ch, start, length);
-
- if (infoState == null)
- return;
-
- switch (infoState) {
-
- case PROTOCOL:
- current.protocol = str;
- break;
-
- case HOSTNAME:
- current.hostname = str;
- break;
-
- case PORT:
- current.port = Integer.parseInt(str);
- break;
-
- case PASSWORD:
- current.password = str;
- break;
-
- }
-
- }
-
-
- }
-
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.GuacamoleSession;
-import net.sourceforge.guacamole.net.authentication.GuacamoleSessionProvider;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-public class BasicGuacamoleSessionProvider implements GuacamoleSessionProvider {
-
- public GuacamoleSession createSession(HttpSession session) throws GuacamoleException {
-
- // Retrieve authorized config data from session
- BasicLogin.AuthorizedConfiguration config = (BasicLogin.AuthorizedConfiguration)
- session.getAttribute("BASIC-LOGIN-AUTH");
-
- // If no data, not authorized
- if (config == null)
- throw new GuacamoleException("Unauthorized");
-
- // Configure session from authorized config info
- GuacamoleSession guacSession = new GuacamoleSession(session);
- guacSession.setConnection(config.getProtocol(), config.getHostname(), config.getPort());
- if (config.getPassword() != null)
- guacSession.setPassword(config.getPassword());
-
- // Return authorized session
- return guacSession;
-
- }
-
-}
+++ /dev/null
-
-package net.sourceforge.guacamole.net.authentication.basic;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.Configuration;
-
-public class BasicLogin extends HttpServlet {
-
- private Config config;
-
- @Override
- public void init() throws ServletException {
- try {
- config = new Config();
- }
- catch (GuacamoleException e) {
- throw new ServletException(e);
- }
- }
-
-
- private class Config extends Configuration {
-
- private AuthenticationProvider authProvider;
-
- public Config() throws GuacamoleException {
-
- // Get auth provider instance
- try {
- String authProviderClassName = readParameter("auth-provider");
- Object obj = Class.forName(authProviderClassName).getConstructor().newInstance();
- if (!(obj instanceof AuthenticationProvider))
- throw new GuacamoleException("Specified session provider class is not a GuacamoleSessionProvider");
-
- authProvider = (AuthenticationProvider) obj;
- }
- catch (ClassNotFoundException e) {
- throw new GuacamoleException("Session provider class not found", e);
- }
- catch (NoSuchMethodException e) {
- throw new GuacamoleException("Default constructor for session provider not present", e);
- }
- catch (SecurityException e) {
- throw new GuacamoleException("Creation of session provider disallowed; check your security settings", e);
- }
- catch (InstantiationException e) {
- throw new GuacamoleException("Unable to instantiate session provider", e);
- }
- catch (IllegalAccessException e) {
- throw new GuacamoleException("Unable to access default constructor of session provider", e);
- }
- catch (InvocationTargetException e) {
- throw new GuacamoleException("Internal error in constructor of session provider", e.getTargetException());
- }
-
- }
-
- public AuthenticationProvider getAuthenticationProvider() {
- return authProvider;
- }
-
- }
-
- public static interface AuthenticationProvider {
- public AuthorizedConfiguration getAuthorizedConfiguration(String username, String password) throws GuacamoleException;
- }
-
- // Added to session when session validated
- public static class AuthorizedConfiguration {
-
- private String protocol;
- private String hostname;
- private int port;
- private String password;
-
- public AuthorizedConfiguration(String protocol, String hostname, int port, String password) {
- this.protocol = protocol;
- this.hostname = hostname;
- this.port = port;
- this.password = password;
- }
-
- public String getHostname() {
- return hostname;
- }
-
- public String getPassword() {
- return password;
- }
-
- public int getPort() {
- return port;
- }
-
- public String getProtocol() {
- return protocol;
- }
-
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-
- // Retrieve username and password from parms
- String username = req.getParameter("username");
- String password = req.getParameter("password");
-
- // Validate username and password
- try {
-
- AuthorizedConfiguration info = config.getAuthenticationProvider().getAuthorizedConfiguration(username, password);
- if (info != null) {
-
- // Store authorized configuration
- HttpSession session = req.getSession(true);
- session.setAttribute(
- "BASIC-LOGIN-AUTH",
- info
- );
-
- // Success
- return;
-
- }
-
- // Report "forbidden" on any failure
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Login invalid");
- }
- catch (GuacamoleException e) {
- throw new ServletException("Error validating credentials", e);
- }
-
- }
-
-
-}
+++ /dev/null
-package net.sourceforge.guacamole.net.tunnel;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import net.sourceforge.guacamole.GuacamoleException;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import net.sourceforge.guacamole.net.GuacamoleServlet;
-
-import net.sourceforge.guacamole.net.GuacamoleSession;
-
-public class Connect extends GuacamoleServlet {
-
- @Override
- protected boolean shouldCreateSession() {
- return true;
- }
-
- @Override
- protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
-
- // Disconnect if already connected
- if (session.isConnected())
- session.disconnect();
-
- // Obtain new connection
- session.connect();
-
- // Send data
- try {
- char[] connect = session.getConnectMessage().toCharArray();
- session.getClient().write(connect, 0, connect.length);
- session.getClient().authorize();
- }
- catch (GuacamoleException e) {
- throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e);
- }
-
- }
-
-}
-
+++ /dev/null
-package net.sourceforge.guacamole.net.tunnel;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import net.sourceforge.guacamole.GuacamoleException;
-
-import java.io.Reader;
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import net.sourceforge.guacamole.net.GuacamoleServlet;
-
-import net.sourceforge.guacamole.net.GuacamoleSession;
-
-public class Inbound extends GuacamoleServlet {
-
- @Override
- protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
-
- session.getClient().waitForAuthorization();
-
- // Send data
- try {
-
- Reader input = request.getReader();
- char[] buffer = new char[8192];
-
- int length;
- while ((length = input.read(buffer, 0, buffer.length)) != -1)
- session.getClient().write(buffer, 0, length);
-
- }
- catch (IOException e) {
- throw new GuacamoleException("I/O Error sending data to server: " + e.getMessage(), e);
- }
- catch (GuacamoleException e) {
- throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e);
- }
-
- }
-
-}
-
+++ /dev/null
-package net.sourceforge.guacamole.net.tunnel;
-
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-import java.io.Writer;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.concurrent.locks.ReentrantLock;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import net.sourceforge.guacamole.Client;
-import net.sourceforge.guacamole.net.GuacamoleServlet;
-import net.sourceforge.guacamole.GuacamoleException;
-import net.sourceforge.guacamole.net.GuacamoleSession;
-
-
-public class Outbound extends GuacamoleServlet {
-
- @Override
- protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
-
- session.getClient().waitForAuthorization();
-
- ReentrantLock instructionStreamLock = session.getInstructionStreamLock();
- instructionStreamLock.lock();
-
- try {
-
- response.setContentType("text/plain");
- Writer out = response.getWriter();
-
- try {
-
- // Query new update from server
- Client client = session.getClient();
-
- // For all messages, until another stream is ready (we send at least one message)
- char[] message;
- while ((message = client.read()) != null) {
-
- // Get message output bytes
- out.write(message, 0, message.length);
- out.flush();
- response.flushBuffer();
-
- // No more messages another stream can take over
- if (instructionStreamLock.hasQueuedThreads())
- break;
-
- }
-
- if (message == null) {
- session.disconnect();
- throw new GuacamoleException("Disconnected.");
- }
-
- }
- catch (GuacamoleException e) {
- out.write("error:" + e.getMessage() + ";");
- out.flush();
- response.flushBuffer();
- }
-
- // End-of-instructions marker
- out.write(';');
- out.flush();
- response.flushBuffer();
-
- }
- catch (UnsupportedEncodingException e) {
- throw new GuacamoleException("UTF-8 not supported by Java.", e);
- }
- catch (IOException e) {
- throw new GuacamoleException("I/O error writing to servlet output stream.", e);
- }
- finally {
- instructionStreamLock.unlock();
- }
-
- }
-
-}
-