mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
Now using true inbound instruction stream ... no more event-specific servlets.
This commit is contained in:
@@ -19,17 +19,14 @@ package net.sourceforge.guacamole;
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import net.sourceforge.guacamole.instruction.Instruction;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
import net.sourceforge.guacamole.event.KeyEvent;
|
||||
import net.sourceforge.guacamole.event.PointerEvent;
|
||||
|
||||
public abstract class Client {
|
||||
|
||||
public abstract void send(KeyEvent event) throws GuacamoleException;
|
||||
public abstract void send(PointerEvent event) throws GuacamoleException;
|
||||
public abstract void setClipboard(String clipboard) throws GuacamoleException;
|
||||
public abstract void write(char[] chunk, int off, int len) throws GuacamoleException;
|
||||
public abstract char[] read() throws GuacamoleException;
|
||||
public abstract void disconnect() throws GuacamoleException;
|
||||
public abstract Instruction nextInstruction(boolean blocking) throws GuacamoleException;
|
||||
|
||||
}
|
||||
|
@@ -31,7 +31,6 @@ import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import net.sourceforge.guacamole.instruction.Instruction;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
import net.sourceforge.guacamole.event.EventQueue;
|
||||
import net.sourceforge.guacamole.event.EventHandler;
|
||||
@@ -57,64 +56,9 @@ public class GuacamoleClient extends Client {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static final int EVENT_DEADLINE = 500;
|
||||
|
||||
private EventQueue<KeyEvent> keyEvents = new EventQueue<KeyEvent>(new EventHandler<KeyEvent>() {
|
||||
|
||||
public void handle(KeyEvent event) throws IOException {
|
||||
int pressed = 0;
|
||||
if (event.getPressed()) pressed = 1;
|
||||
|
||||
output.write("key:" + event.getKeySym() + "," + pressed + ";");
|
||||
output.flush();
|
||||
}
|
||||
|
||||
}, EVENT_DEADLINE);
|
||||
|
||||
private EventQueue<PointerEvent> pointerEvents = new EventQueue<PointerEvent>(new EventHandler<PointerEvent>() {
|
||||
|
||||
public void handle(PointerEvent event) throws IOException {
|
||||
int mask = 0;
|
||||
if (event.isLeftButtonPressed()) mask |= 1;
|
||||
if (event.isMiddleButtonPressed()) mask |= 2;
|
||||
if (event.isRightButtonPressed()) mask |= 4;
|
||||
if (event.isUpButtonPressed()) mask |= 8;
|
||||
if (event.isDownButtonPressed()) mask |= 16;
|
||||
|
||||
|
||||
output.write("mouse:" + event.getX() + "," + event.getY() + "," + mask + ";");
|
||||
output.flush();
|
||||
}
|
||||
|
||||
}, EVENT_DEADLINE);
|
||||
|
||||
|
||||
public void send(KeyEvent event) throws GuacamoleException {
|
||||
|
||||
public void write(char[] chunk, int off, int len) throws GuacamoleException {
|
||||
try {
|
||||
keyEvents.add(event);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void send(PointerEvent event) throws GuacamoleException {
|
||||
|
||||
try {
|
||||
pointerEvents.add(event);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setClipboard(String clipboard) throws GuacamoleException {
|
||||
try {
|
||||
output.write("clipboard:" + Instruction.escape(clipboard) + ";");
|
||||
output.write(chunk, off, len);
|
||||
output.flush();
|
||||
}
|
||||
catch (IOException e) {
|
||||
@@ -134,7 +78,7 @@ public class GuacamoleClient extends Client {
|
||||
private int usedLength = 0;
|
||||
private char[] buffer = new char[20000];
|
||||
|
||||
public Instruction nextInstruction(boolean blocking) throws GuacamoleException {
|
||||
public char[] read() throws GuacamoleException {
|
||||
|
||||
try {
|
||||
|
||||
@@ -164,20 +108,15 @@ public class GuacamoleClient extends Client {
|
||||
if (readChar == ';') {
|
||||
|
||||
// Get instruction
|
||||
final String instruction = new String(buffer, 0, i+1);
|
||||
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 wrapped in Instruction class
|
||||
return new Instruction() {
|
||||
|
||||
public String toString() {
|
||||
return instruction;
|
||||
}
|
||||
|
||||
};
|
||||
// Return instruction string
|
||||
return chunk;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,39 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction;
|
||||
|
||||
/*
|
||||
* 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 ClipboardInstruction extends Instruction {
|
||||
|
||||
private String data;
|
||||
|
||||
public ClipboardInstruction(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "clipboard:" + escape(getData()) + ";";
|
||||
}
|
||||
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction;
|
||||
|
||||
/*
|
||||
* 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 ErrorInstruction extends Instruction {
|
||||
|
||||
private String error;
|
||||
|
||||
public ErrorInstruction(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "error:" + escape(getError()) + ";";
|
||||
}
|
||||
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction;
|
||||
|
||||
/*
|
||||
* 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 Instruction {
|
||||
|
||||
|
||||
// All Instructions must provide a toString() implementation
|
||||
// which returns the properly formatted instruction:
|
||||
// OPCODE:parm1,parm2,...,parmN;
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
public static String escape(String str) {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i=0; i<str.length(); i++) {
|
||||
|
||||
char c = str.charAt(i);
|
||||
|
||||
switch (c) {
|
||||
case ',':
|
||||
sb.append("\\c");
|
||||
break;
|
||||
|
||||
case ';':
|
||||
sb.append("\\s");
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
sb.append("\\\\");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction;
|
||||
|
||||
/*
|
||||
* 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 NameInstruction extends Instruction {
|
||||
|
||||
private String name;
|
||||
|
||||
public NameInstruction(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name:" + escape(getName()) + ";";
|
||||
}
|
||||
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction;
|
||||
|
||||
/*
|
||||
* 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 SizeInstruction extends Instruction {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public SizeInstruction(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "size:"
|
||||
+ getWidth() + ","
|
||||
+ getHeight() + ";";
|
||||
}
|
||||
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction.framebuffer;
|
||||
|
||||
/*
|
||||
* 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.instruction.Instruction;
|
||||
|
||||
public class CopyRectInstruction extends Instruction {
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
private final int srcX;
|
||||
private final int srcY;
|
||||
|
||||
public CopyRectInstruction(int x, int y, int width, int height, int srcX, int srcY) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.srcX = srcX;
|
||||
this.srcY = srcY;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getSrcX() {
|
||||
return srcX;
|
||||
}
|
||||
|
||||
public int getSrcY() {
|
||||
return srcY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "copy:"
|
||||
+ getSrcX() + ","
|
||||
+ getSrcY() + ","
|
||||
+ getWidth() + ","
|
||||
+ getHeight() + ","
|
||||
+ getX() + ","
|
||||
+ getY() + ";";
|
||||
}
|
||||
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
package net.sourceforge.guacamole.instruction.framebuffer;
|
||||
|
||||
import net.sourceforge.guacamole.net.Base64;
|
||||
|
||||
/*
|
||||
* 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.instruction.Instruction;
|
||||
|
||||
public class CursorInstruction extends Instruction {
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private PNGImage image;
|
||||
|
||||
public CursorInstruction(int x, int y, PNGImage image) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public PNGImage getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return getImage().getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return getImage().getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "cursor:"
|
||||
+ getX() + ","
|
||||
+ getY() + ","
|
||||
+ Base64.toString(getImage().getData()) + ";";
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
|
||||
package net.sourceforge.guacamole.instruction.framebuffer;
|
||||
|
||||
/*
|
||||
* 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.instruction.Instruction;
|
||||
|
||||
public class DrawRectInstruction extends Instruction {
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int color;
|
||||
|
||||
public DrawRectInstruction(int x, int y, int width, int height, int color) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "rect:"
|
||||
+ getX() + ","
|
||||
+ getY() + ","
|
||||
+ getWidth() + ","
|
||||
+ getHeight() + ","
|
||||
+ String.format("#%06X", getColor()) + ";";
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
package net.sourceforge.guacamole.instruction.framebuffer;
|
||||
|
||||
/*
|
||||
* 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.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
|
||||
public class PNGImage {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private byte[] data;
|
||||
|
||||
public PNGImage(BufferedImage image) throws GuacamoleException {
|
||||
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
writeImage(image, bos);
|
||||
bos.flush();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleException("I/O Error while creating PNG.", e);
|
||||
}
|
||||
|
||||
data = bos.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
private static void writeImage(BufferedImage image, OutputStream outputStream) throws GuacamoleException, IOException {
|
||||
|
||||
// Obtain list of image writers
|
||||
// If no such writers exist, fail with error, exit.
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByMIMEType("image/png");
|
||||
if (!writers.hasNext())
|
||||
throw new GuacamoleException("No useful image writers found.");
|
||||
|
||||
// Obtain JPEG writer
|
||||
ImageWriter imageWriter = writers.next();
|
||||
|
||||
// Setup image parameters (including compression quality)
|
||||
/*ImageWriteParam imageParameters = new JPEGImageWriteParam(Locale.ENGLISH);
|
||||
imageParameters.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
imageParameters.setCompressionQuality(0.6f); // 60% quality, currently...
|
||||
imageParameters.setProgressiveMode(ImageWriteParam.MODE_DEFAULT);*/
|
||||
|
||||
ImageOutputStream out = ImageIO.createImageOutputStream(outputStream);
|
||||
|
||||
// Write image
|
||||
imageWriter.setOutput(out);
|
||||
imageWriter.write(null, new IIOImage(image, null, null), null/*imageParameters*/);
|
||||
imageWriter.dispose();
|
||||
|
||||
out.flush();
|
||||
}
|
||||
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
package net.sourceforge.guacamole.instruction.framebuffer;
|
||||
|
||||
/*
|
||||
* 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.instruction.Instruction;
|
||||
import net.sourceforge.guacamole.net.Base64;
|
||||
|
||||
public class PNGInstruction extends Instruction {
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private PNGImage image;
|
||||
|
||||
public PNGInstruction(int x, int y, PNGImage image) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public PNGImage getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return getImage().getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return getImage().getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "png:"
|
||||
+ getX() + ","
|
||||
+ getY() + ","
|
||||
+ Base64.toString(getImage().getData()) + ";";
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -27,7 +27,6 @@ import javax.servlet.http.HttpSessionBindingListener;
|
||||
import net.sourceforge.guacamole.Client;
|
||||
import net.sourceforge.guacamole.GuacamoleClient;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
import net.sourceforge.guacamole.instruction.Instruction;
|
||||
import net.sourceforge.guacamole.event.KeyEvent;
|
||||
import net.sourceforge.guacamole.event.PointerEvent;
|
||||
|
||||
@@ -59,26 +58,18 @@ public class GuacamoleSession {
|
||||
}
|
||||
}
|
||||
|
||||
public void send(KeyEvent event) throws GuacamoleException {
|
||||
client.send(event);
|
||||
public void write(char[] data, int off, int len) throws GuacamoleException {
|
||||
client.write(data, off, len);
|
||||
}
|
||||
|
||||
public void send(PointerEvent event) throws GuacamoleException {
|
||||
client.send(event);
|
||||
}
|
||||
|
||||
public void setClipboard(String clipboard) throws GuacamoleException {
|
||||
client.setClipboard(clipboard);
|
||||
public char[] read() throws GuacamoleException {
|
||||
return client.read();
|
||||
}
|
||||
|
||||
public void disconnect() throws GuacamoleException {
|
||||
client.disconnect();
|
||||
}
|
||||
|
||||
public Instruction nextInstruction(boolean blocking) throws GuacamoleException {
|
||||
return client.nextInstruction(blocking);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public GuacamoleSession(HttpSession session) throws GuacamoleException {
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
package net.sourceforge.guacamole.net.input;
|
||||
|
||||
/*
|
||||
@@ -23,46 +22,37 @@ 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
public class Inbound extends XMLGuacamoleServlet {
|
||||
|
||||
/**
|
||||
* Servlet which sets the clipboard data.
|
||||
*
|
||||
* This servlet takes one parameter:
|
||||
* data: The data to set the clipboard to.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
|
||||
public class Clipboard extends XMLGuacamoleServlet {
|
||||
|
||||
@Override
|
||||
protected void handleRequest(GuacamoleSession session, ServletRequest request, Element root) throws GuacamoleException {
|
||||
|
||||
// Send data
|
||||
try {
|
||||
|
||||
// Read data from request body
|
||||
Reader reader = request.getReader();
|
||||
StringBuilder data = new StringBuilder();
|
||||
Reader input = request.getReader();
|
||||
char[] buffer = new char[8192];
|
||||
|
||||
int codepoint;
|
||||
while ((codepoint = reader.read()) != -1)
|
||||
data.appendCodePoint(codepoint);
|
||||
int length;
|
||||
while ((length = input.read(buffer, 0, buffer.length)) != -1)
|
||||
session.getClient().write(buffer, 0, length);
|
||||
|
||||
// Set clipboard
|
||||
session.getClient().setClipboard(data.toString());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleException("I/O error sending clipboard to server: " + e.getMessage(), e);
|
||||
throw new GuacamoleException("I/O Error sending data to server: " + e.getMessage(), e);
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
throw new GuacamoleException("Error sending clipboard to server: " + e.getMessage(), e);
|
||||
throw new GuacamoleException("Error sending data to server: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,66 +0,0 @@
|
||||
|
||||
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 net.sourceforge.guacamole.event.KeyEvent;
|
||||
|
||||
import net.sourceforge.guacamole.net.GuacamoleSession;
|
||||
import net.sourceforge.guacamole.net.XMLGuacamoleServlet;
|
||||
|
||||
/**
|
||||
* Servlet which accepts keyboard input events, forwards these events to the
|
||||
* client associated with the session, and returns the result (if any)
|
||||
* to the HTTP client via XML.
|
||||
*
|
||||
* This servlet takes three parameters:
|
||||
* index: The event index. As HTTP requests may arrive out of order,
|
||||
* this index provides the event queue with a means of sorting
|
||||
* events, and determining if events are missing. The first
|
||||
* event has index 0.
|
||||
* pressed: Whether the key was pressed (1) or released (0).
|
||||
* keysym: The integer representing the corresponding X11 keysym.
|
||||
*
|
||||
* @author Michael Jumper
|
||||
*/
|
||||
|
||||
public class Key extends XMLGuacamoleServlet {
|
||||
|
||||
@Override
|
||||
protected void handleRequest(GuacamoleSession session, ServletRequest request, Element root) throws GuacamoleException {
|
||||
|
||||
// Event parameters
|
||||
int index = Integer.parseInt(request.getParameter("index"));
|
||||
boolean pressed = request.getParameter("pressed").equals("1");
|
||||
int keysym = Integer.parseInt(request.getParameter("keysym"));
|
||||
|
||||
// Send/queue event
|
||||
try {
|
||||
session.getClient().send(new KeyEvent(index, keysym, pressed));
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
throw new GuacamoleException("Error sending key event to server: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,63 +0,0 @@
|
||||
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 net.sourceforge.guacamole.event.PointerEvent;
|
||||
|
||||
import net.sourceforge.guacamole.net.GuacamoleSession;
|
||||
import net.sourceforge.guacamole.net.XMLGuacamoleServlet;
|
||||
|
||||
public class Pointer extends XMLGuacamoleServlet {
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleRequest(GuacamoleSession session, ServletRequest request, Element root) throws GuacamoleException {
|
||||
// Event parameters
|
||||
String[] events = request.getParameterValues("event");
|
||||
|
||||
for (String event : events) {
|
||||
|
||||
String[] parameters = event.split(",");
|
||||
|
||||
int index = Integer.parseInt(parameters[0]);
|
||||
|
||||
int x = Integer.parseInt(parameters[1]);
|
||||
int y = Integer.parseInt(parameters[2]);
|
||||
|
||||
boolean left = parameters[3].equals("1");
|
||||
boolean middle = parameters[4].equals("1");
|
||||
boolean right = parameters[5].equals("1");
|
||||
boolean up = parameters[6].equals("1");
|
||||
boolean down = parameters[7].equals("1");
|
||||
|
||||
// Store event
|
||||
try {
|
||||
session.getClient().send(new PointerEvent(index, left, middle, right, up, down, x, y));
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
throw new GuacamoleException("Error sending pointer event to server: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,12 +18,9 @@ package net.sourceforge.guacamole.net.output;
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -31,8 +28,6 @@ import net.sourceforge.guacamole.Client;
|
||||
import net.sourceforge.guacamole.net.GuacamoleServlet;
|
||||
import net.sourceforge.guacamole.GuacamoleException;
|
||||
import net.sourceforge.guacamole.net.GuacamoleSession;
|
||||
import net.sourceforge.guacamole.instruction.Instruction;
|
||||
import net.sourceforge.guacamole.instruction.ErrorInstruction;
|
||||
|
||||
|
||||
public class InstructionStream extends GuacamoleServlet {
|
||||
@@ -46,36 +41,7 @@ public class InstructionStream extends GuacamoleServlet {
|
||||
try {
|
||||
|
||||
response.setContentType("text/plain");
|
||||
OutputStream out = response.getOutputStream();
|
||||
|
||||
// Compress if enabled and supported by browser
|
||||
if (session.getConfiguration().getCompressStream()) {
|
||||
|
||||
String encodingHeader = request.getHeader("Accept-Encoding");
|
||||
if (encodingHeader != null) {
|
||||
|
||||
String[] encodings = encodingHeader.split(",");
|
||||
for (String encoding : encodings) {
|
||||
|
||||
// Use gzip if supported
|
||||
if (encoding.equals("gzip")) {
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
out = new GZIPOutputStream(out);
|
||||
break;
|
||||
}
|
||||
|
||||
// Use deflate if supported
|
||||
if (encoding.equals("deflate")) {
|
||||
response.setHeader("Content-Encoding", "deflate");
|
||||
out = new DeflaterOutputStream(out);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Writer out = response.getWriter();
|
||||
|
||||
try {
|
||||
|
||||
@@ -83,12 +49,11 @@ public class InstructionStream extends GuacamoleServlet {
|
||||
Client client = session.getClient();
|
||||
|
||||
// For all messages, until another stream is ready (we send at least one message)
|
||||
Instruction message = client.nextInstruction(true); // Block until first message is read
|
||||
while (message != null) {
|
||||
char[] message;
|
||||
while ((message = client.read()) != null) {
|
||||
|
||||
// Get message output bytes
|
||||
byte[] outputBytes = message.toString().getBytes("UTF-8");
|
||||
out.write(outputBytes);
|
||||
out.write(message, 0, message.length);
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
|
||||
@@ -96,14 +61,11 @@ public class InstructionStream extends GuacamoleServlet {
|
||||
if (instructionStreamLock.hasQueuedThreads())
|
||||
break;
|
||||
|
||||
message = client.nextInstruction(false); // Read remaining messages, do not block.
|
||||
}
|
||||
|
||||
}
|
||||
catch (GuacamoleException e) {
|
||||
Instruction message = new ErrorInstruction(e.getMessage());
|
||||
byte[] outputBytes = message.toString().getBytes("UTF-8");
|
||||
out.write(outputBytes);
|
||||
out.write("error:" + e.getMessage() + ";");
|
||||
out.flush();
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
@@ -53,31 +53,13 @@
|
||||
<url-pattern>/instructions</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet>
|
||||
<description>Clipboard input servlet.</description>
|
||||
<servlet-name>Clipboard</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.input.Clipboard</servlet-class>
|
||||
<description>Input servlet.</description>
|
||||
<servlet-name>Inbound</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.input.Inbound</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Clipboard</servlet-name>
|
||||
<url-pattern>/clipboard</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet>
|
||||
<description>Key input servlet.</description>
|
||||
<servlet-name>Key</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.input.Key</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Key</servlet-name>
|
||||
<url-pattern>/key</url-pattern>
|
||||
</servlet-mapping>
|
||||
<servlet>
|
||||
<description>Pointer input servlet.</description>
|
||||
<servlet-name>Pointer</servlet-name>
|
||||
<servlet-class>net.sourceforge.guacamole.net.input.Pointer</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Pointer</servlet-name>
|
||||
<url-pattern>/pointer</url-pattern>
|
||||
<servlet-name>Inbound</servlet-name>
|
||||
<url-pattern>/inbound</url-pattern>
|
||||
</servlet-mapping>
|
||||
<security-constraint>
|
||||
<display-name>Guacamole Access Restrictions</display-name>
|
||||
|
@@ -46,9 +46,6 @@ function VNCClient(display) {
|
||||
|| currentState == STATE_WAITING;
|
||||
}
|
||||
|
||||
var keyIndex = 0;
|
||||
var xmlIndex = 0;
|
||||
|
||||
// Layers
|
||||
var background = null;
|
||||
var cursor = null;
|
||||
@@ -118,17 +115,7 @@ function VNCClient(display) {
|
||||
this.enableKeyboard();
|
||||
|
||||
function sendKeyEvent(pressed, keysym) {
|
||||
|
||||
// Do not send requests if not connected
|
||||
if (!isConnected())
|
||||
return;
|
||||
|
||||
var key_xmlhttprequest = new XMLHttpRequest();
|
||||
key_xmlhttprequest.open("GET",
|
||||
"key?index=" + (keyIndex++)
|
||||
+ "&pressed=" + pressed
|
||||
+ "&keysym=" + keysym);
|
||||
key_xmlhttprequest.send(null);
|
||||
sendMessage("key:" + keysym + "," + pressed + ";");
|
||||
}
|
||||
|
||||
this.pressKey = function(keysym) {
|
||||
@@ -170,47 +157,63 @@ function VNCClient(display) {
|
||||
);
|
||||
|
||||
|
||||
var sendingMouseEvents = 0;
|
||||
var mouseEventBuffer = "";
|
||||
|
||||
function sendMouseState(mouseState) {
|
||||
|
||||
// Build mask
|
||||
var buttonMask = 0;
|
||||
if (mouseState.getLeft()) buttonMask |= 1;
|
||||
if (mouseState.getMiddle()) buttonMask |= 2;
|
||||
if (mouseState.getRight()) buttonMask |= 4;
|
||||
if (mouseState.getUp()) buttonMask |= 8;
|
||||
if (mouseState.getDown()) buttonMask |= 16;
|
||||
|
||||
// Send message
|
||||
sendMessage("mouse:" + mouseState.getX() + "," + mouseState.getY() + "," + buttonMask + ";");
|
||||
}
|
||||
|
||||
var sendingMessages = 0;
|
||||
var outputMessageBuffer = "";
|
||||
|
||||
function sendMessage(message) {
|
||||
|
||||
// Do not send requests if not connected
|
||||
if (!isConnected())
|
||||
return;
|
||||
|
||||
// Add event to queue, restart send loop if finished.
|
||||
if (mouseEventBuffer.length > 0) mouseEventBuffer += "&";
|
||||
mouseEventBuffer += "event=" + mouseState.toString();
|
||||
if (sendingMouseEvents == 0)
|
||||
sendPendingMouseEvents();
|
||||
outputMessageBuffer += message;
|
||||
if (sendingMessages == 0)
|
||||
sendPendingMessages();
|
||||
|
||||
}
|
||||
|
||||
function sendPendingMouseEvents() {
|
||||
function sendPendingMessages() {
|
||||
|
||||
// Do not send requests if not connected
|
||||
if (!isConnected())
|
||||
return;
|
||||
|
||||
if (mouseEventBuffer.length > 0) {
|
||||
if (outputMessageBuffer.length > 0) {
|
||||
|
||||
sendingMouseEvents = 1;
|
||||
sendingMessages = 1;
|
||||
|
||||
var mouse_xmlhttprequest = new XMLHttpRequest();
|
||||
mouse_xmlhttprequest.open("GET", "pointer?" + mouseEventBuffer);
|
||||
mouseEventBuffer = ""; // Clear buffer
|
||||
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.
|
||||
mouse_xmlhttprequest.onreadystatechange = function() {
|
||||
if (mouse_xmlhttprequest.readyState == 4)
|
||||
sendPendingMouseEvents();
|
||||
message_xmlhttprequest.onreadystatechange = function() {
|
||||
if (message_xmlhttprequest.readyState == 4)
|
||||
sendPendingMessages();
|
||||
}
|
||||
|
||||
mouse_xmlhttprequest.send(null);
|
||||
message_xmlhttprequest.send(outputMessageBuffer);
|
||||
outputMessageBuffer = ""; // Clear buffer
|
||||
|
||||
}
|
||||
else
|
||||
sendingMouseEvents = 0;
|
||||
sendingMessages = 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -225,10 +228,7 @@ function VNCClient(display) {
|
||||
if (!isConnected())
|
||||
return;
|
||||
|
||||
var clipboard_xmlhttprequest = new XMLHttpRequest();
|
||||
clipboard_xmlhttprequest.open("POST", "clipboard");
|
||||
clipboard_xmlhttprequest.send(data);
|
||||
|
||||
sendMessage("clipboard:" + escapeGuacamoleString(data) + ";");
|
||||
}
|
||||
|
||||
|
||||
@@ -387,6 +387,28 @@ function VNCClient(display) {
|
||||
|
||||
}
|
||||
|
||||
function escapeGuacamoleString(str) {
|
||||
|
||||
var escapedString = "";
|
||||
|
||||
for (var i=0; i<str.length; i++) {
|
||||
|
||||
var c = str.charAt(i);
|
||||
if (c == ",")
|
||||
escapedString += "\\c";
|
||||
else if (c == ";")
|
||||
escapedString += "\\s";
|
||||
else if (c == "\\")
|
||||
escapedString += "\\\\";
|
||||
else
|
||||
escapedString += c;
|
||||
|
||||
}
|
||||
|
||||
return escapedString;
|
||||
|
||||
}
|
||||
|
||||
function unescapeGuacamoleString(str) {
|
||||
|
||||
var unescapedString = "";
|
||||
|
Reference in New Issue
Block a user