mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +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.listener.TunnelCloseListener;
|
||||
import org.glyptodon.guacamole.net.event.listener.TunnelConnectListener;
|
||||
import org.glyptodon.guacamole.properties.GuacamoleProperties;
|
||||
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -329,8 +330,19 @@ public class BasicTunnelRequestUtility {
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@@ -71,8 +71,11 @@ public class CaptureClipboard extends AuthenticatingHttpServlet {
|
||||
|
||||
// Send clipboard contents
|
||||
try {
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().print(clipboard.waitForContents(CLIPBOARD_TIMEOUT));
|
||||
synchronized (clipboard) {
|
||||
clipboard.waitForContents(CLIPBOARD_TIMEOUT);
|
||||
response.setContentType(clipboard.getMimetype());
|
||||
response.getOutputStream().write(clipboard.getContents());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException("Unable to send clipboard contents", e);
|
||||
|
@@ -31,11 +31,36 @@ package org.glyptodon.guacamole.net.basic;
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -45,38 +70,84 @@ public class ClipboardState {
|
||||
* Returns the current clipboard contents.
|
||||
* @return The current clipboard contents
|
||||
*/
|
||||
public synchronized String getContents() {
|
||||
public synchronized byte[] getContents() {
|
||||
return contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current clipboard contents.
|
||||
* @param contents The contents to assign to the clipboard.
|
||||
* Returns the mimetype of the current clipboard contents.
|
||||
* @return The mimetype of the current clipboard contents.
|
||||
*/
|
||||
public synchronized void setContents(String contents) {
|
||||
this.contents = contents;
|
||||
last_update = System.currentTimeMillis();
|
||||
this.notifyAll();
|
||||
public synchronized String getMimetype() {
|
||||
return mimetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait up to the given timeout for new clipboard data. If data more recent
|
||||
* than the timeout period is available, return that.
|
||||
* Begins a new update of the clipboard contents. The actual contents will
|
||||
* 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.
|
||||
* @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
|
||||
if (System.currentTimeMillis() - last_update > timeout) {
|
||||
try {
|
||||
this.wait(timeout);
|
||||
return true;
|
||||
}
|
||||
catch (InterruptedException e) { /* ignore */ }
|
||||
}
|
||||
|
||||
return getContents();
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
package org.glyptodon.guacamole.net.basic;
|
||||
|
||||
import java.util.List;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import org.glyptodon.guacamole.GuacamoleException;
|
||||
import org.glyptodon.guacamole.io.GuacamoleReader;
|
||||
import org.glyptodon.guacamole.protocol.GuacamoleInstruction;
|
||||
@@ -45,6 +46,11 @@ public class MonitoringGuacamoleReader implements GuacamoleReader {
|
||||
*/
|
||||
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
|
||||
* read by the given GuacamoleReader, firing events when specific
|
||||
@@ -84,11 +90,31 @@ public class MonitoringGuacamoleReader implements GuacamoleReader {
|
||||
if (instruction == null)
|
||||
return null;
|
||||
|
||||
// If clipboard changed, notify listeners
|
||||
// If clipboard changing, reset clipboard state
|
||||
if (instruction.getOpcode().equals("clipboard")) {
|
||||
List<String> args = instruction.getArgs();
|
||||
if (args.size() >= 1)
|
||||
clipboard.setContents(args.get(0));
|
||||
if (args.size() >= 2) {
|
||||
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;
|
||||
|
Reference in New Issue
Block a user