diff --git a/guacamole/client/Makefile b/guacamole/client/Makefile
new file mode 100644
index 000000000..760266a47
--- /dev/null
+++ b/guacamole/client/Makefile
@@ -0,0 +1,11 @@
+
+.PHONY: client clean
+
+all: client
+
+client:
+ ant war
+
+clean:
+ ant clean
+
diff --git a/guacamole/client/ant/build.properties b/guacamole/client/ant/build.properties
new file mode 100644
index 000000000..5fa6766cc
--- /dev/null
+++ b/guacamole/client/ant/build.properties
@@ -0,0 +1,13 @@
+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.0rc1
+tar.dir=guacamole-${guac.version}
+tar.src.dir=guacamole-src
+
diff --git a/guacamole/client/build.xml b/guacamole/client/build.xml
new file mode 100644
index 000000000..88685640e
--- /dev/null
+++ b/guacamole/client/build.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/guacamole/client/doc/example/guacamole-users.xml b/guacamole/client/doc/example/guacamole-users.xml
new file mode 100644
index 000000000..50bb77a12
--- /dev/null
+++ b/guacamole/client/doc/example/guacamole-users.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/guacamole/client/doc/example/guacamole.xml b/guacamole/client/doc/example/guacamole.xml
new file mode 100644
index 000000000..1c005f4da
--- /dev/null
+++ b/guacamole/client/doc/example/guacamole.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/guacamole/client/src/net/sourceforge/guacamole/Client.java b/guacamole/client/src/net/sourceforge/guacamole/Client.java
new file mode 100644
index 000000000..93000aaf4
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/Client.java
@@ -0,0 +1,32 @@
+
+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 .
+ */
+
+import net.sourceforge.guacamole.GuacamoleException;
+import net.sourceforge.guacamole.event.KeyEvent;
+import net.sourceforge.guacamole.event.PointerEvent;
+
+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;
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/GuacamoleClient.java b/guacamole/client/src/net/sourceforge/guacamole/GuacamoleClient.java
new file mode 100644
index 000000000..f79e8a1b4
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/GuacamoleClient.java
@@ -0,0 +1,133 @@
+
+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 .
+ */
+
+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;
+import net.sourceforge.guacamole.event.EventQueue;
+import net.sourceforge.guacamole.event.EventHandler;
+import net.sourceforge.guacamole.event.KeyEvent;
+import net.sourceforge.guacamole.event.PointerEvent;
+
+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);
+ }
+
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/GuacamoleException.java b/guacamole/client/src/net/sourceforge/guacamole/GuacamoleException.java
new file mode 100644
index 000000000..3a09c5ac7
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/GuacamoleException.java
@@ -0,0 +1,36 @@
+
+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 .
+ */
+
+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);
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/event/Event.java b/guacamole/client/src/net/sourceforge/guacamole/event/Event.java
new file mode 100644
index 000000000..8fa047f1b
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/event/Event.java
@@ -0,0 +1,55 @@
+
+package net.sourceforge.guacamole.event;
+
+/*
+ * 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 .
+ */
+
+public abstract class Event implements Comparable {
+
+ private long time;
+ private int index;
+
+ public Event(int index) {
+ this.time = System.currentTimeMillis();
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public int hashCode() {
+ return index;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (getClass() != o.getClass()) return false;
+ return getIndex() == ((Event) o).getIndex();
+ }
+
+ public int compareTo(Event e) {
+ return getIndex() - e.getIndex();
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/event/EventHandler.java b/guacamole/client/src/net/sourceforge/guacamole/event/EventHandler.java
new file mode 100644
index 000000000..bae027a2e
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/event/EventHandler.java
@@ -0,0 +1,28 @@
+
+package net.sourceforge.guacamole.event;
+
+/*
+ * 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 .
+ */
+
+import java.io.IOException;
+
+public interface EventHandler {
+
+ public void handle(E e) throws IOException;
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/event/EventQueue.java b/guacamole/client/src/net/sourceforge/guacamole/event/EventQueue.java
new file mode 100644
index 000000000..a1d7a5246
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/event/EventQueue.java
@@ -0,0 +1,191 @@
+
+package net.sourceforge.guacamole.event;
+
+/*
+ * 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 .
+ */
+
+import java.io.IOException;
+import java.util.PriorityQueue;
+
+public class EventQueue {
+
+ private int nextIndex = 0;
+ private final PriorityQueue queue = new PriorityQueue();
+ private EventHandler handler;
+
+ private final int deadline;
+
+ private AutoflushThread autoflush = new AutoflushThread();
+
+ private class AutoflushThread extends Thread {
+
+ private IOException error;
+ private long deadline;
+ private static final long ONE_YEAR = 31536000;
+
+ private boolean killed = false;
+
+ public AutoflushThread() {
+ this.deadline = ONE_YEAR;
+ start();
+ }
+
+ public void run() {
+ while (!killed) {
+ try {
+ if (deadline > 0) sleep(deadline);
+ dropPendingEvents();
+ flush();
+ }
+ catch (InterruptedException e) {
+ // Interrupt indicates event handled, or thread killed
+ if (killed) return;
+ }
+ catch (IOException e) {
+ error = e;
+ break;
+ }
+ }
+ }
+
+ public void setDeadline(long deadline) {
+ this.deadline = deadline;
+ interrupt();
+ }
+
+ public void checkError() throws IOException {
+ if (error != null) throw error;
+ }
+
+ public void kill() {
+ killed = true;
+ interrupt();
+ }
+
+ }
+
+ public void close() {
+ autoflush.kill();
+ }
+
+ // Starts autoflush wait thread for any waiting events on the queue
+ private void startDeadlineAutoflush() {
+ synchronized (queue) {
+
+ // No need to autoflush if nothing waiting
+ if (queue.size() == 0) return;
+
+ // Get waiting event
+ E waiting = queue.peek();
+
+ if (waiting != null) {
+ long untilDeadline = deadline + waiting.getTime() - System.currentTimeMillis();
+
+ // Start autoflush thread which waits for time remaining until next
+ // event's deadline.
+ autoflush.setDeadline(untilDeadline);
+ }
+ else
+ autoflush.setDeadline(AutoflushThread.ONE_YEAR);
+
+ }
+ }
+
+ public EventQueue(EventHandler handler, int deadline) {
+ this.handler = handler;
+ this.deadline = deadline;
+ }
+
+ public void add(E event) throws IOException {
+ synchronized (queue) {
+
+ autoflush.checkError();
+
+ if (event.getIndex() < nextIndex) {
+ //System.err.println("Past event dropped.");
+ return;
+ }
+
+ if (event == null)
+ throw new Error("Cannot add null event.");
+
+ queue.add(event);
+ }
+
+ flush();
+ }
+
+ private E next() {
+ synchronized (queue) {
+ // If no events, return nothing.
+ if (queue.size() == 0) return null;
+
+ // If still waiting for true next event, return nothing.
+ E event = queue.peek();
+ if (event.getIndex() != nextIndex)
+ return null;
+
+ // If event found, expect next event, remove and return current.
+ queue.remove();
+ nextIndex++;
+ return event;
+ }
+ }
+
+ // Return number of waiting events
+ public int getWaiting() {
+ synchronized (queue) {
+ // If no events, then none waiting.
+ if (queue.size() == 0) return 0;
+
+ // If we have the next event, then none waiting.
+ E event = queue.peek();
+ if (event.getIndex() == nextIndex)
+ return 0;
+
+ // Otherwise, all events are waiting.
+ return queue.size();
+ }
+ }
+
+ // Stop waiting for any unreceived events
+ private void dropPendingEvents() {
+ synchronized (queue) {
+ // If no events, nothing needs to be changed;
+ if (queue.size() == 0) return;
+
+ // Otherwise, update nextIndex to index of next event
+ E event = queue.peek();
+ nextIndex = event.getIndex();
+ }
+ }
+
+ // Attempts to flush queue
+ // If any events remain, an autoflush thread is started.
+ private void flush() throws IOException {
+ synchronized (queue) {
+ E nextEvent;
+ while ((nextEvent = next()) != null)
+ handler.handle(nextEvent);
+ }
+
+ // Start autoflush thread for any remaining events.
+ startDeadlineAutoflush();
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/event/KeyEvent.java b/guacamole/client/src/net/sourceforge/guacamole/event/KeyEvent.java
new file mode 100644
index 000000000..a287dd448
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/event/KeyEvent.java
@@ -0,0 +1,39 @@
+package net.sourceforge.guacamole.event;
+
+/*
+ * 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 .
+ */
+
+public class KeyEvent extends Event {
+
+ private int keysym;
+ private boolean pressed;
+
+ public KeyEvent(int index, int keysym, boolean pressed) {
+ super(index);
+ this.keysym = keysym;
+ this.pressed = pressed;
+ }
+
+ public int getKeySym() {
+ return keysym;
+ }
+
+ public boolean getPressed() {
+ return pressed;
+ }
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/event/PointerEvent.java b/guacamole/client/src/net/sourceforge/guacamole/event/PointerEvent.java
new file mode 100644
index 000000000..05692ada1
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/event/PointerEvent.java
@@ -0,0 +1,69 @@
+package net.sourceforge.guacamole.event;
+
+/*
+ * 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 .
+ */
+
+public class PointerEvent extends Event {
+
+ private boolean leftButtonPressed;
+ private boolean middleButtonPressed;
+ private boolean rightButtonPressed;
+ private boolean upButtonPressed;
+ private boolean downButtonPressed;
+ private int x;
+ private int y;
+
+ public PointerEvent(int index, boolean leftButtonPressed, boolean middleButtonPressed, boolean rightButtonPressed, boolean upButtonPressed, boolean downButtonPressed, int x, int y) {
+ super(index);
+ this.leftButtonPressed = leftButtonPressed;
+ this.middleButtonPressed = middleButtonPressed;
+ this.rightButtonPressed = rightButtonPressed;
+ this.upButtonPressed = upButtonPressed;
+ this.downButtonPressed = downButtonPressed;
+ this.x = x;
+ this.y = y;
+ }
+
+ public boolean isLeftButtonPressed() {
+ return leftButtonPressed;
+ }
+
+ public boolean isMiddleButtonPressed() {
+ return middleButtonPressed;
+ }
+
+ public boolean isRightButtonPressed() {
+ return rightButtonPressed;
+ }
+
+ public boolean isUpButtonPressed() {
+ return upButtonPressed;
+ }
+
+ public boolean isDownButtonPressed() {
+ return downButtonPressed;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/Base64.java b/guacamole/client/src/net/sourceforge/guacamole/net/Base64.java
new file mode 100644
index 000000000..1fa0676e0
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/Base64.java
@@ -0,0 +1,66 @@
+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 .
+ */
+
+public class Base64 {
+
+ private static String characters =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+ public static String toString(byte[] data) {
+
+ StringBuffer buff = new StringBuffer();
+ for (int i=0; i> 2
+ ));
+
+ buff.append(characters.charAt(
+ ((a & 0x03) << 4) |
+ ((b & 0xF0) >> 4)
+ ));
+
+ if (i+1 < data.length)
+ buff.append(characters.charAt(
+ ((b & 0x0F) << 2) |
+ ((c & 0xC0) >> 6)
+ ));
+ else
+ buff.append('=');
+
+ if (i+2 < data.length)
+ buff.append(characters.charAt(
+ (c & 0x3F)
+ ));
+ else
+ buff.append('=');
+
+ }
+
+ return buff.toString();
+
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/Configuration.java b/guacamole/client/src/net/sourceforge/guacamole/net/Configuration.java
new file mode 100644
index 000000000..b8bc8d057
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/Configuration.java
@@ -0,0 +1,130 @@
+
+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 .
+ */
+
+import javax.servlet.ServletContext;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public abstract class Configuration {
+
+ private ServletContext context;
+
+ protected String humanReadableList(Object... values) {
+
+ String list = "";
+ for (int i=0; i= 1)
+ list += ", ";
+
+ if (i == values.length -1)
+ list += " or ";
+
+ list += "\"" + values[i] + "\"";
+ }
+
+ return list;
+
+ }
+
+ protected String readParameter(String name, String defaultValue, String... allowedValues) throws GuacamoleException {
+
+ String value = context.getInitParameter(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 = context.getInitParameter(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 = context.getInitParameter(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);
+ }
+
+ }
+
+ public Configuration(ServletContext context) {
+ this.context = context;
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleConfiguration.java b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleConfiguration.java
new file mode 100644
index 000000000..b357bb57c
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleConfiguration.java
@@ -0,0 +1,46 @@
+
+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 .
+ */
+
+import javax.servlet.ServletContext;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public class GuacamoleConfiguration extends Configuration {
+
+ private String hostname;
+ private int port;
+
+ public GuacamoleConfiguration(ServletContext context) throws GuacamoleException {
+
+ super(context);
+
+ hostname = context.getInitParameter("hostname");
+ port = readIntParameter("port", null);
+
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleServlet.java b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleServlet.java
new file mode 100644
index 000000000..5c0a55dd9
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleServlet.java
@@ -0,0 +1,62 @@
+
+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 .
+ */
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import net.sourceforge.guacamole.GuacamoleException;
+
+public abstract class GuacamoleServlet extends HttpServlet {
+
+ @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 {
+ GuacamoleSession session = new GuacamoleSession(request.getSession(shouldCreateSession()));
+ handleRequest(session, request, response);
+ }
+
+ protected abstract void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException;
+
+ protected boolean shouldCreateSession() {
+ return false;
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleSession.java b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleSession.java
new file mode 100644
index 000000000..d00f4037a
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/GuacamoleSession.java
@@ -0,0 +1,147 @@
+
+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 .
+ */
+
+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;
+import net.sourceforge.guacamole.event.KeyEvent;
+import net.sourceforge.guacamole.event.PointerEvent;
+
+public class GuacamoleSession {
+
+ private GuacamoleConfiguration config;
+ private final HttpSession session;
+ private Client client;
+ private ReentrantLock instructionStreamLock;
+
+ private class SessionClient extends Client implements HttpSessionBindingListener {
+
+ private Client client;
+
+ public SessionClient(Client client) {
+ this.client = client;
+ }
+
+ 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
+ ServletContext context = session.getServletContext();
+ config = new GuacamoleConfiguration(context);
+
+ client = (Client) 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.getHostname(),
+ config.getPort()
+ )
+ );
+
+ 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 Client 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;
+ }
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/XMLGuacamoleServlet.java b/guacamole/client/src/net/sourceforge/guacamole/net/XMLGuacamoleServlet.java
new file mode 100644
index 000000000..2fef0c647
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/XMLGuacamoleServlet.java
@@ -0,0 +1,132 @@
+
+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 .
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import net.sourceforge.guacamole.Client;
+import net.sourceforge.guacamole.GuacamoleException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public abstract class XMLGuacamoleServlet extends GuacamoleServlet {
+
+ @Override
+ protected final void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ response.setContentType("text/xml");
+ response.setHeader("Cache-Control", "no-store");
+ response.setHeader("Pragma", "no-cache");
+ response.setDateHeader("Expires", 0);
+
+ try {
+
+ // Create document
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ Document document = documentBuilder.newDocument();
+
+ // Root element
+ Element root = document.createElement("guacamole");
+ document.appendChild(root);
+
+ try {
+ handleRequest(session, request, root);
+ }
+ catch (Throwable t) {
+ addFatalError(root, t.getMessage());
+
+ // FATAL error ... try to disconnect
+ if (session != null) {
+ Client client = session.getClient();
+ try {
+ if (client != null)
+ client.disconnect();
+ }
+ catch (GuacamoleException e) {
+ addFatalError(root, e.getMessage());
+ }
+ }
+ }
+
+ // Set up transformer
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ //transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+
+ // Write XML using transformer
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ StreamResult result = new StreamResult(bos);
+ DOMSource source = new DOMSource(document);
+ transformer.transform(source, result);
+
+
+ bos.flush();
+
+ byte[] xmlData = bos.toByteArray();
+ response.setContentLength(xmlData.length);
+ OutputStream outputStream = response.getOutputStream();
+ outputStream.write(xmlData);
+
+ // Close stream
+ outputStream.close();
+ }
+ catch (ParserConfigurationException e) {
+ throw new GuacamoleException(e);
+ }
+ catch (TransformerConfigurationException e) {
+ throw new GuacamoleException(e);
+ }
+ catch (TransformerException e) {
+ throw new GuacamoleException(e);
+ }
+ catch (IOException e) {
+ throw new GuacamoleException(e);
+ }
+
+
+ }
+
+ private void addFatalError(Element root, String message) {
+ Element error = root.getOwnerDocument().createElement("error");
+ error.setAttribute("type", "fatal");
+ error.setTextContent(message);
+ root.appendChild(error);
+ }
+
+ protected abstract void handleRequest(GuacamoleSession session, ServletRequest request, Element root) throws GuacamoleException;
+
+}
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/input/Inbound.java b/guacamole/client/src/net/sourceforge/guacamole/net/input/Inbound.java
new file mode 100644
index 000000000..7be7bc6cb
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/input/Inbound.java
@@ -0,0 +1,64 @@
+package net.sourceforge.guacamole.net.input;
+
+/*
+ * 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 .
+ */
+
+import javax.servlet.ServletRequest;
+import net.sourceforge.guacamole.GuacamoleException;
+import org.w3c.dom.Element;
+
+import java.io.Reader;
+import java.io.IOException;
+
+import net.sourceforge.guacamole.net.GuacamoleSession;
+import net.sourceforge.guacamole.net.XMLGuacamoleServlet;
+
+public class Inbound extends XMLGuacamoleServlet {
+
+ protected boolean shouldCreateSession() {
+ return true;
+ }
+
+ @Override
+ protected void handleRequest(GuacamoleSession session, ServletRequest request, Element root) throws GuacamoleException {
+
+ if (!session.isConnected())
+ session.connect();
+
+ // 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);
+ }
+
+ }
+
+}
+
diff --git a/guacamole/client/src/net/sourceforge/guacamole/net/output/InstructionStream.java b/guacamole/client/src/net/sourceforge/guacamole/net/output/InstructionStream.java
new file mode 100644
index 000000000..27e5e65de
--- /dev/null
+++ b/guacamole/client/src/net/sourceforge/guacamole/net/output/InstructionStream.java
@@ -0,0 +1,97 @@
+package net.sourceforge.guacamole.net.output;
+
+/*
+ * 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 .
+ */
+
+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 InstructionStream extends GuacamoleServlet {
+
+ @Override
+ protected void handleRequest(GuacamoleSession session, HttpServletRequest request, HttpServletResponse response) throws GuacamoleException {
+
+ 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();
+ }
+
+ }
+
+}
+
diff --git a/guacamole/client/tunnel.php b/guacamole/client/tunnel.php
deleted file mode 100644
index eb9b41ce7..000000000
--- a/guacamole/client/tunnel.php
+++ /dev/null
@@ -1,35 +0,0 @@
-
diff --git a/guacamole/client/web/WEB-INF/web.xml b/guacamole/client/web/WEB-INF/web.xml
new file mode 100644
index 000000000..c61e6b842
--- /dev/null
+++ b/guacamole/client/web/WEB-INF/web.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ index.html
+
+
+
+ 30
+
+
+
+
+ Instruction stream servlet.
+ InstructionStream
+ net.sourceforge.guacamole.net.output.InstructionStream
+
+
+ InstructionStream
+ /instructions
+
+
+ Input servlet.
+ Inbound
+ net.sourceforge.guacamole.net.input.Inbound
+
+
+ Inbound
+ /inbound
+
+
+ Guacamole Access Restrictions
+
+ Guacamole
+ All servlets/pages within Guacamole.
+ /*
+
+
+ Only allow Guacamole users access.
+ guacamole
+
+
+
+ BASIC
+ Guacamole
+
+
+ Guacamole
+ guacamole
+
+
diff --git a/guacamole/client/agpl-3.0-standalone.html b/guacamole/client/web/agpl-3.0-standalone.html
similarity index 100%
rename from guacamole/client/agpl-3.0-standalone.html
rename to guacamole/client/web/agpl-3.0-standalone.html
diff --git a/guacamole/client/guacamole.css b/guacamole/client/web/guacamole.css
similarity index 100%
rename from guacamole/client/guacamole.css
rename to guacamole/client/web/guacamole.css
diff --git a/guacamole/client/images/agpl-logo.png b/guacamole/client/web/images/agpl-logo.png
similarity index 100%
rename from guacamole/client/images/agpl-logo.png
rename to guacamole/client/web/images/agpl-logo.png
diff --git a/guacamole/client/images/checker.png b/guacamole/client/web/images/checker.png
similarity index 100%
rename from guacamole/client/images/checker.png
rename to guacamole/client/web/images/checker.png
diff --git a/guacamole/client/images/guacamole-64-icon.png b/guacamole/client/web/images/guacamole-64-icon.png
similarity index 100%
rename from guacamole/client/images/guacamole-64-icon.png
rename to guacamole/client/web/images/guacamole-64-icon.png
diff --git a/guacamole/client/images/guacamole-icon-64.png b/guacamole/client/web/images/guacamole-icon-64.png
similarity index 100%
rename from guacamole/client/images/guacamole-icon-64.png
rename to guacamole/client/web/images/guacamole-icon-64.png
diff --git a/guacamole/client/images/guacamole-logo.png b/guacamole/client/web/images/guacamole-logo.png
similarity index 100%
rename from guacamole/client/images/guacamole-logo.png
rename to guacamole/client/web/images/guacamole-logo.png
diff --git a/guacamole/client/images/mouse/blank.cur b/guacamole/client/web/images/mouse/blank.cur
similarity index 100%
rename from guacamole/client/images/mouse/blank.cur
rename to guacamole/client/web/images/mouse/blank.cur
diff --git a/guacamole/client/images/mouse/blank.gif b/guacamole/client/web/images/mouse/blank.gif
similarity index 100%
rename from guacamole/client/images/mouse/blank.gif
rename to guacamole/client/web/images/mouse/blank.gif
diff --git a/guacamole/client/images/mouse/dot.gif b/guacamole/client/web/images/mouse/dot.gif
similarity index 100%
rename from guacamole/client/images/mouse/dot.gif
rename to guacamole/client/web/images/mouse/dot.gif
diff --git a/guacamole/client/images/noguacamole-logo.png b/guacamole/client/web/images/noguacamole-logo.png
similarity index 100%
rename from guacamole/client/images/noguacamole-logo.png
rename to guacamole/client/web/images/noguacamole-logo.png
diff --git a/guacamole/client/images/noimage92.png b/guacamole/client/web/images/noimage92.png
similarity index 100%
rename from guacamole/client/images/noimage92.png
rename to guacamole/client/web/images/noimage92.png
diff --git a/guacamole/client/images/spinner92.gif b/guacamole/client/web/images/spinner92.gif
similarity index 100%
rename from guacamole/client/images/spinner92.gif
rename to guacamole/client/web/images/spinner92.gif
diff --git a/guacamole/client/index.html b/guacamole/client/web/index.html
similarity index 100%
rename from guacamole/client/index.html
rename to guacamole/client/web/index.html
diff --git a/guacamole/client/javascript/guacamole.js b/guacamole/client/web/javascript/guacamole.js
similarity index 87%
rename from guacamole/client/javascript/guacamole.js
rename to guacamole/client/web/javascript/guacamole.js
index 460c99270..f82fa58e6 100644
--- a/guacamole/client/javascript/guacamole.js
+++ b/guacamole/client/web/javascript/guacamole.js
@@ -186,9 +186,38 @@ function VNCClient(display) {
// Add event to queue, restart send loop if finished.
outputMessageBuffer += message;
+ if (sendingMessages == 0)
+ sendPendingMessages();
}
+ function sendPendingMessages() {
+
+ if (outputMessageBuffer.length > 0) {
+
+ sendingMessages = 1;
+
+ var message_xmlhttprequest = new XMLHttpRequest();
+ message_xmlhttprequest.open("POST", "inbound");
+ message_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ message_xmlhttprequest.setRequestHeader("Content-length", outputMessageBuffer.length);
+
+ // Once response received, send next queued event.
+ message_xmlhttprequest.onreadystatechange = function() {
+ if (message_xmlhttprequest.readyState == 4)
+ sendPendingMessages();
+ }
+
+ message_xmlhttprequest.send(outputMessageBuffer);
+ outputMessageBuffer = ""; // Clear buffer
+
+ }
+ else
+ sendingMessages = 0;
+
+ }
+
+
/*****************************************/
/*** Clipboard ***/
/*****************************************/
@@ -272,7 +301,7 @@ function VNCClient(display) {
function parseResponse() {
// Start next request as soon as possible
- if (xmlhttprequest.readyState >= 2 && nextRequest == null && uuid)
+ if (xmlhttprequest.readyState >= 2 && nextRequest == null)
nextRequest = makeRequest();
// Parse stream when data is received and when complete.
@@ -349,19 +378,10 @@ function VNCClient(display) {
function makeRequest() {
- if (uuid)
- outputMessageBuffer = "resume:" + uuid + ";" + outputMessageBuffer;
-
- outputMessageBuffer += "pause;";
-
// Download self
var xmlhttprequest = new XMLHttpRequest();
- xmlhttprequest.open("POST", "tunnel.php");
- xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- xmlhttprequest.setRequestHeader("Content-length", outputMessageBuffer.length);
-
- xmlhttprequest.send(outputMessageBuffer);
- outputMessageBuffer = "";
+ xmlhttprequest.open("POST", "instructions");
+ xmlhttprequest.send(null);
return xmlhttprequest;
@@ -418,14 +438,8 @@ function VNCClient(display) {
}
- var uuid = null;
-
var instructionHandlers = {
- "uuid": function(parameters) {
- uuid = parameters[0];
- },
-
"error": function(parameters) {
showError(unescapeGuacamoleString(parameters[0]));
},
@@ -545,8 +559,16 @@ function VNCClient(display) {
this.connect = function() {
+ var message = "connect;";
+
setState(STATE_CONNECTING);
- sendMessage("connect;");
+
+ // Send connect message (synchronously... as necessary until handoff is implemented)
+ var connect_xmlhttprequest = new XMLHttpRequest();
+ connect_xmlhttprequest.open("POST", "inbound", false);
+ connect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ connect_xmlhttprequest.setRequestHeader("Content-length", message.length);
+ connect_xmlhttprequest.send(message);
// Start reading data
setState(STATE_WAITING);
@@ -561,9 +583,15 @@ function VNCClient(display) {
if (currentState != STATE_DISCONNECTED
&& currentState != STATE_DISCONNECTING) {
+ var message = "disconnect;";
setState(STATE_DISCONNECTING);
- sendMessage("disconnect;");
+ // Send disconnect message (synchronously... as necessary until handoff is implemented)
+ var disconnect_xmlhttprequest = new XMLHttpRequest();
+ disconnect_xmlhttprequest.open("POST", "inbound", false);
+ disconnect_xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ disconnect_xmlhttprequest.setRequestHeader("Content-length", message.length);
+ disconnect_xmlhttprequest.send(message);
setState(STATE_DISCONNECTED);
}
diff --git a/guacamole/client/javascript/keyboard.js b/guacamole/client/web/javascript/keyboard.js
similarity index 100%
rename from guacamole/client/javascript/keyboard.js
rename to guacamole/client/web/javascript/keyboard.js
diff --git a/guacamole/client/javascript/keymap.js b/guacamole/client/web/javascript/keymap.js
similarity index 100%
rename from guacamole/client/javascript/keymap.js
rename to guacamole/client/web/javascript/keymap.js
diff --git a/guacamole/client/javascript/layer.js b/guacamole/client/web/javascript/layer.js
similarity index 100%
rename from guacamole/client/javascript/layer.js
rename to guacamole/client/web/javascript/layer.js
diff --git a/guacamole/client/javascript/message.js b/guacamole/client/web/javascript/message.js
similarity index 100%
rename from guacamole/client/javascript/message.js
rename to guacamole/client/web/javascript/message.js
diff --git a/guacamole/client/javascript/mouse.js b/guacamole/client/web/javascript/mouse.js
similarity index 100%
rename from guacamole/client/javascript/mouse.js
rename to guacamole/client/web/javascript/mouse.js
diff --git a/guacamole/client/javascript/oskeyboard.js b/guacamole/client/web/javascript/oskeyboard.js
similarity index 100%
rename from guacamole/client/javascript/oskeyboard.js
rename to guacamole/client/web/javascript/oskeyboard.js
diff --git a/guacamole/client/keyboard.css b/guacamole/client/web/keyboard.css
similarity index 100%
rename from guacamole/client/keyboard.css
rename to guacamole/client/web/keyboard.css
diff --git a/guacamole/client/layouts/en-us-qwerty.xml b/guacamole/client/web/layouts/en-us-qwerty.xml
similarity index 100%
rename from guacamole/client/layouts/en-us-qwerty.xml
rename to guacamole/client/web/layouts/en-us-qwerty.xml