Revert "Removed old Java client, added new experimental PHP client."
This reverts commit 95f9da3a71c3e804dfe0457504cdbc0371c9b626.
This commit is contained in:
1 parent
8f3e6a7340
commit
a1440075d0
46 files changed
+1625
-55
No files matched your search
@@ -0,0 +1,11 @@
|
||||
|
||||
.PHONY: client clean
|
||||
|
||||
all: client
|
||||
|
||||
client:
|
||||
ant war
|
||||
|
||||
clean:
|
||||
ant clean
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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}">
|
||||
<exclude name="META-INF/**"/>
|
||||
</fileset>
|
||||
</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.xml"/>
|
||||
<fileset file="${doc.dir}/example/guacamole-users.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>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
|
||||
<tomcat-users>
|
||||
<role rolename="guacamole"/>
|
||||
<user username="guacamole" password="changeme" roles="guacamole"/>
|
||||
</tomcat-users>
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Guacamole - Pure JavaScript/HTML VNC Client
|
||||
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/>.
|
||||
-->
|
||||
|
||||
<Context antiJARLocking="true" path="/guacamole" docBase="/var/lib/guacamole/guacamole.war">
|
||||
|
||||
<!-- Change the lines below to match your VNC server -->
|
||||
<Parameter name="host" value="localhost"/>
|
||||
<Parameter name="port" value="5900"/>
|
||||
|
||||
|
||||
<!-- Password (VNC Authentication)
|
||||
|
||||
Uncomment and change the line below if your VNC server is
|
||||
password protected. -->
|
||||
|
||||
<!--
|
||||
<Parameter name="password" value="PASSWORD"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Bits-per-pixel
|
||||
|
||||
This parameter controls what color depth should be
|
||||
requested from the VNC server.
|
||||
|
||||
Valid values are "8", "16", or "24". -->
|
||||
|
||||
<!--
|
||||
<Parameter name="bpp" value="24"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Output (to web client) bits-per-pixel.
|
||||
|
||||
This parameter controls what color depth will be used
|
||||
in the images sent to the web-based client.
|
||||
|
||||
Valid values are "8" or "24". -->
|
||||
|
||||
<!--
|
||||
<Parameter name="output-bpp" value="24"/>
|
||||
-->
|
||||
|
||||
|
||||
<!-- Swap red and blue components. Some VNC servers may not honor
|
||||
the client's request for certain pixel formats, instead
|
||||
returning RGB as BGR.
|
||||
|
||||
If colors look strange (blues look red or brown) when using
|
||||
Guacamole, uncomment and set this parameter to true. -->
|
||||
|
||||
<!--
|
||||
<Parameter name="swap-red-blue" value="false"/>
|
||||
-->
|
||||
|
||||
<Realm className="org.apache.catalina.realm.MemoryRealm" pathname="conf/guacamole-users.xml"/>
|
||||
</Context>
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -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 <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;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public abstract class Event implements Comparable<Event> {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface EventHandler<E extends Event> {
|
||||
|
||||
public void handle(E e) throws IOException;
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
public class EventQueue<E extends Event> {
|
||||
|
||||
private int nextIndex = 0;
|
||||
private final PriorityQueue<E> queue = new PriorityQueue<E>();
|
||||
private EventHandler<E> 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<E> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class Base64 {
|
||||
|
||||
private static String characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
|
||||
public static String toString(byte[] data) {
|
||||
|
||||
StringBuffer buff = new StringBuffer();
|
||||
for (int i=0; i<data.length; i+=3) {
|
||||
|
||||
int a = data[i];
|
||||
int b = 0; if (i+1 < data.length) b = data[i+1];
|
||||
int c = 0; if (i+2 < data.length) c = data[i+2];
|
||||
|
||||
buff.append(characters.charAt(
|
||||
(a & 0xFC) >> 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<values.length; i++) {
|
||||
|
||||
if (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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <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;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <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 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
# End all output buffers
|
||||
while (@ob_end_flush());
|
||||
|
||||
# Create socket
|
||||
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
|
||||
if (!$socket) {
|
||||
die("error:Could not create socket.;");
|
||||
}
|
||||
|
||||
# Open connection
|
||||
$result = socket_connect($socket, "localhost", 2222);
|
||||
if (!$result) {
|
||||
die("error:Could not connect: " . socket_strerror(socket_last_error()) . ";");
|
||||
}
|
||||
|
||||
# Socket should block
|
||||
socket_set_block($socket);
|
||||
|
||||
# Write all data sent here
|
||||
socket_write($socket, file_get_contents("php://input"));
|
||||
|
||||
# Read until EOF
|
||||
while (($buffer = socket_read($socket, 8192))) {
|
||||
echo $buffer;
|
||||
flush();
|
||||
}
|
||||
|
||||
echo ";";
|
||||
|
||||
# Close socket
|
||||
socket_close($socket);
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Guacamole - Pure JavaScript/HTML VNC Client
|
||||
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/>.
|
||||
-->
|
||||
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
|
||||
<!-- Basic config -->
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
</welcome-file-list>
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<!-- Servlets -->
|
||||
<servlet>
|
||||
<description>Instruction stream servlet.</description>
|
||||
<servlet-name>InstructionStream</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.output.InstructionStream</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>InstructionStream</servlet-name>
|
||||
<url-pattern>/instructions</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet>
|
||||
<description>Input servlet.</description>
|
||||
<servlet-name>Inbound</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.input.Inbound</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Inbound</servlet-name>
|
||||
<url-pattern>/inbound</url-pattern>
|
||||
</servlet-mapping>
|
||||
<security-constraint>
|
||||
<display-name>Guacamole Access Restrictions</display-name>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Guacamole</web-resource-name>
|
||||
<description>All servlets/pages within Guacamole.</description>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<description>Only allow Guacamole users access.</description>
|
||||
<role-name>guacamole</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
<login-config>
|
||||
<auth-method>BASIC</auth-method>
|
||||
<realm-name>Guacamole</realm-name>
|
||||
</login-config>
|
||||
<security-role>
|
||||
<description>Guacamole</description>
|
||||
<role-name>guacamole</role-name>
|
||||
</security-role>
|
||||
</web-app>
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+48
-20
@@ -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);
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user