mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 21:51:23 +00:00
GUAC-609: Migrate outbound clipboard integration to new streams.
This commit is contained in:
@@ -42,6 +42,7 @@ import org.glyptodon.guacamole.net.event.TunnelCloseEvent;
|
|||||||
import org.glyptodon.guacamole.net.event.TunnelConnectEvent;
|
import org.glyptodon.guacamole.net.event.TunnelConnectEvent;
|
||||||
import org.glyptodon.guacamole.net.event.listener.TunnelCloseListener;
|
import org.glyptodon.guacamole.net.event.listener.TunnelCloseListener;
|
||||||
import org.glyptodon.guacamole.net.event.listener.TunnelConnectListener;
|
import org.glyptodon.guacamole.net.event.listener.TunnelConnectListener;
|
||||||
|
import org.glyptodon.guacamole.properties.GuacamoleProperties;
|
||||||
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -329,8 +330,19 @@ public class BasicTunnelRequestUtility {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GuacamoleReader acquireReader() {
|
public GuacamoleReader acquireReader() {
|
||||||
// Monitor instructions which pertain to server-side events
|
|
||||||
return new MonitoringGuacamoleReader(clipboard, super.acquireReader());
|
// Monitor instructions which pertain to server-side events, if necessary
|
||||||
|
try {
|
||||||
|
if (GuacamoleProperties.getProperty(CaptureClipboard.INTEGRATION_ENABLED, false))
|
||||||
|
return new MonitoringGuacamoleReader(clipboard, super.acquireReader());
|
||||||
|
}
|
||||||
|
catch (GuacamoleException e) {
|
||||||
|
logger.warn("Clipboard integration disabled due to error.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass through by default.
|
||||||
|
return super.acquireReader();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -71,8 +71,11 @@ public class CaptureClipboard extends AuthenticatingHttpServlet {
|
|||||||
|
|
||||||
// Send clipboard contents
|
// Send clipboard contents
|
||||||
try {
|
try {
|
||||||
response.setContentType("text/plain");
|
synchronized (clipboard) {
|
||||||
response.getWriter().print(clipboard.waitForContents(CLIPBOARD_TIMEOUT));
|
clipboard.waitForContents(CLIPBOARD_TIMEOUT);
|
||||||
|
response.setContentType(clipboard.getMimetype());
|
||||||
|
response.getOutputStream().write(clipboard.getContents());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new GuacamoleServerException("Unable to send clipboard contents", e);
|
throw new GuacamoleServerException("Unable to send clipboard contents", e);
|
||||||
|
@@ -31,11 +31,36 @@ package org.glyptodon.guacamole.net.basic;
|
|||||||
*/
|
*/
|
||||||
public class ClipboardState {
|
public class ClipboardState {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of bytes to track.
|
||||||
|
*/
|
||||||
|
private static final int MAXIMUM_LENGTH = 262144;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mimetype of the current contents.
|
||||||
|
*/
|
||||||
|
private String mimetype = "text/plain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mimetype of the pending contents.
|
||||||
|
*/
|
||||||
|
private String pending_mimetype = "text/plain";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current contents.
|
* The current contents.
|
||||||
*/
|
*/
|
||||||
private String contents = "";
|
private byte[] contents = new byte[0];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pending clipboard contents.
|
||||||
|
*/
|
||||||
|
private final byte[] pending = new byte[MAXIMUM_LENGTH];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the pending data, in bytes.
|
||||||
|
*/
|
||||||
|
private int pending_length = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp of the last contents update.
|
* The timestamp of the last contents update.
|
||||||
*/
|
*/
|
||||||
@@ -45,38 +70,84 @@ public class ClipboardState {
|
|||||||
* Returns the current clipboard contents.
|
* Returns the current clipboard contents.
|
||||||
* @return The current clipboard contents
|
* @return The current clipboard contents
|
||||||
*/
|
*/
|
||||||
public synchronized String getContents() {
|
public synchronized byte[] getContents() {
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current clipboard contents.
|
* Returns the mimetype of the current clipboard contents.
|
||||||
* @param contents The contents to assign to the clipboard.
|
* @return The mimetype of the current clipboard contents.
|
||||||
*/
|
*/
|
||||||
public synchronized void setContents(String contents) {
|
public synchronized String getMimetype() {
|
||||||
this.contents = contents;
|
return mimetype;
|
||||||
last_update = System.currentTimeMillis();
|
|
||||||
this.notifyAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait up to the given timeout for new clipboard data. If data more recent
|
* Begins a new update of the clipboard contents. The actual contents will
|
||||||
* than the timeout period is available, return that.
|
* not be saved until commit() is called.
|
||||||
|
*
|
||||||
|
* @param mimetype The mimetype of the contents being added.
|
||||||
|
*/
|
||||||
|
public synchronized void begin(String mimetype) {
|
||||||
|
pending_length = 0;
|
||||||
|
this.pending_mimetype = mimetype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends the given data to the clipboard contents.
|
||||||
|
*
|
||||||
|
* @param data The raw data to append.
|
||||||
|
*/
|
||||||
|
public synchronized void append(byte[] data) {
|
||||||
|
|
||||||
|
// Calculate size of copy
|
||||||
|
int length = data.length;
|
||||||
|
int remaining = pending.length - pending_length;
|
||||||
|
if (remaining < length)
|
||||||
|
length = remaining;
|
||||||
|
|
||||||
|
// Append data
|
||||||
|
System.arraycopy(data, 0, pending, pending_length, length);
|
||||||
|
pending_length += length;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits the pending contents to the clipboard, notifying any threads
|
||||||
|
* waiting for clipboard updates.
|
||||||
|
*/
|
||||||
|
public synchronized void commit() {
|
||||||
|
|
||||||
|
// Commit contents
|
||||||
|
mimetype = pending_mimetype;
|
||||||
|
contents = new byte[pending_length];
|
||||||
|
System.arraycopy(pending, 0, contents, 0, pending_length);
|
||||||
|
|
||||||
|
// Notify of update
|
||||||
|
last_update = System.currentTimeMillis();
|
||||||
|
this.notifyAll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait up to the given timeout for new clipboard data.
|
||||||
*
|
*
|
||||||
* @param timeout The amount of time to wait, in milliseconds.
|
* @param timeout The amount of time to wait, in milliseconds.
|
||||||
* @return The current clipboard contents.
|
* @return true if the contents were updated within the timeframe given,
|
||||||
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
public synchronized String waitForContents(int timeout) {
|
public synchronized boolean waitForContents(int timeout) {
|
||||||
|
|
||||||
// Wait for new contents if it's been a while
|
// Wait for new contents if it's been a while
|
||||||
if (System.currentTimeMillis() - last_update > timeout) {
|
if (System.currentTimeMillis() - last_update > timeout) {
|
||||||
try {
|
try {
|
||||||
this.wait(timeout);
|
this.wait(timeout);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) { /* ignore */ }
|
catch (InterruptedException e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
return getContents();
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
package org.glyptodon.guacamole.net.basic;
|
package org.glyptodon.guacamole.net.basic;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import org.glyptodon.guacamole.GuacamoleException;
|
import org.glyptodon.guacamole.GuacamoleException;
|
||||||
import org.glyptodon.guacamole.io.GuacamoleReader;
|
import org.glyptodon.guacamole.io.GuacamoleReader;
|
||||||
import org.glyptodon.guacamole.protocol.GuacamoleInstruction;
|
import org.glyptodon.guacamole.protocol.GuacamoleInstruction;
|
||||||
@@ -45,6 +46,11 @@ public class MonitoringGuacamoleReader implements GuacamoleReader {
|
|||||||
*/
|
*/
|
||||||
private final ClipboardState clipboard;
|
private final ClipboardState clipboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index of the clipboard stream, if any.
|
||||||
|
*/
|
||||||
|
private String clipboard_stream_index = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new MonitoringGuacamoleReader which watches the instructions
|
* Creates a new MonitoringGuacamoleReader which watches the instructions
|
||||||
* read by the given GuacamoleReader, firing events when specific
|
* read by the given GuacamoleReader, firing events when specific
|
||||||
@@ -84,11 +90,31 @@ public class MonitoringGuacamoleReader implements GuacamoleReader {
|
|||||||
if (instruction == null)
|
if (instruction == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// If clipboard changed, notify listeners
|
// If clipboard changing, reset clipboard state
|
||||||
if (instruction.getOpcode().equals("clipboard")) {
|
if (instruction.getOpcode().equals("clipboard")) {
|
||||||
List<String> args = instruction.getArgs();
|
List<String> args = instruction.getArgs();
|
||||||
if (args.size() >= 1)
|
if (args.size() >= 2) {
|
||||||
clipboard.setContents(args.get(0));
|
clipboard_stream_index = args.get(0);
|
||||||
|
clipboard.begin(args.get(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add clipboard blobs to existing streams
|
||||||
|
else if (instruction.getOpcode().equals("blob")) {
|
||||||
|
List<String> args = instruction.getArgs();
|
||||||
|
if (args.size() >= 2 && args.get(0).equals(clipboard_stream_index)) {
|
||||||
|
String base64 = args.get(1);
|
||||||
|
clipboard.append(DatatypeConverter.parseBase64Binary(base64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate and update clipboard at end of stream
|
||||||
|
else if (instruction.getOpcode().equals("end")) {
|
||||||
|
List<String> args = instruction.getArgs();
|
||||||
|
if (args.size() >= 1 && args.get(0).equals(clipboard_stream_index)) {
|
||||||
|
clipboard.commit();
|
||||||
|
clipboard_stream_index = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instruction;
|
return instruction;
|
||||||
|
Reference in New Issue
Block a user