@@ -548,19 +548,21 @@ void CConnection::handleClipboardCaps(rdr::U32 flags, | |||
void CConnection::handleClipboardRequest(rdr::U32 flags) | |||
{ | |||
if (!(flags & rfb::clipboardUTF8)) | |||
if (!(flags & rfb::clipboardUTF8)) { | |||
vlog.debug("Ignoring clipboard request for unsupported formats 0x%x", flags); | |||
return; | |||
if (!hasLocalClipboard) | |||
} | |||
if (!hasLocalClipboard) { | |||
vlog.debug("Ignoring unexpected clipboard request"); | |||
return; | |||
} | |||
handleClipboardRequest(); | |||
} | |||
void CConnection::handleClipboardPeek(rdr::U32 flags) | |||
{ | |||
if (!hasLocalClipboard) | |||
return; | |||
if (server.clipboardFlags() & rfb::clipboardNotify) | |||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||
writer()->writeClipboardNotify(hasLocalClipboard ? rfb::clipboardUTF8 : 0); | |||
} | |||
void CConnection::handleClipboardNotify(rdr::U32 flags) | |||
@@ -580,8 +582,10 @@ void CConnection::handleClipboardProvide(rdr::U32 flags, | |||
const size_t* lengths, | |||
const rdr::U8* const* data) | |||
{ | |||
if (!(flags & rfb::clipboardUTF8)) | |||
if (!(flags & rfb::clipboardUTF8)) { | |||
vlog.debug("Ignoring clipboard provide with unsupported formats 0x%x", flags); | |||
return; | |||
} | |||
strFree(serverClipboard); | |||
serverClipboard = NULL; | |||
@@ -631,13 +635,25 @@ void CConnection::requestClipboard() | |||
void CConnection::announceClipboard(bool available) | |||
{ | |||
hasLocalClipboard = available; | |||
unsolicitedClipboardAttempt = false; | |||
// Attempt an unsolicited transfer? | |||
if (available && | |||
(server.clipboardSize(rfb::clipboardUTF8) > 0) && | |||
(server.clipboardFlags() & rfb::clipboardProvide)) { | |||
vlog.debug("Attempting unsolicited clipboard transfer..."); | |||
unsolicitedClipboardAttempt = true; | |||
handleClipboardRequest(); | |||
return; | |||
} | |||
if (server.clipboardFlags() & rfb::clipboardNotify) | |||
if (server.clipboardFlags() & rfb::clipboardNotify) { | |||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); | |||
else { | |||
if (available) | |||
handleClipboardRequest(); | |||
return; | |||
} | |||
if (available) | |||
handleClipboardRequest(); | |||
} | |||
void CConnection::sendClipboardData(const char* data) | |||
@@ -646,6 +662,17 @@ void CConnection::sendClipboardData(const char* data) | |||
CharArray filtered(convertCRLF(data)); | |||
size_t sizes[1] = { strlen(filtered.buf) + 1 }; | |||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf }; | |||
if (unsolicitedClipboardAttempt) { | |||
unsolicitedClipboardAttempt = false; | |||
if (sizes[0] > server.clipboardSize(rfb::clipboardUTF8)) { | |||
vlog.debug("Clipboard was too large for unsolicited clipboard transfer"); | |||
if (server.clipboardFlags() & rfb::clipboardNotify) | |||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||
return; | |||
} | |||
} | |||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | |||
} else { | |||
CharArray latin1(utf8ToLatin1(data)); |
@@ -294,6 +294,7 @@ namespace rfb { | |||
char* serverClipboard; | |||
bool hasLocalClipboard; | |||
bool unsolicitedClipboardAttempt; | |||
}; | |||
} | |||
#endif |
@@ -21,6 +21,7 @@ | |||
#include <rfb/Exception.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/CMsgHandler.h> | |||
#include <rfb/clipboardTypes.h> | |||
#include <rfb/screenTypes.h> | |||
static rfb::LogWriter vlog("CMsgHandler"); | |||
@@ -101,6 +102,46 @@ void CMsgHandler::setLEDState(unsigned int state) | |||
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | |||
{ | |||
int i; | |||
vlog.debug("Got server clipboard capabilities:"); | |||
for (i = 0;i < 16;i++) { | |||
if (flags & (1 << i)) { | |||
const char *type; | |||
switch (1 << i) { | |||
case clipboardUTF8: | |||
type = "Plain text"; | |||
break; | |||
case clipboardRTF: | |||
type = "Rich text"; | |||
break; | |||
case clipboardHTML: | |||
type = "HTML"; | |||
break; | |||
case clipboardDIB: | |||
type = "Images"; | |||
break; | |||
case clipboardFiles: | |||
type = "Files"; | |||
break; | |||
default: | |||
vlog.debug(" Unknown format 0x%x", 1 << i); | |||
continue; | |||
} | |||
if (lengths[i] == 0) | |||
vlog.debug(" %s (only notify)", type); | |||
else { | |||
char bytes[1024]; | |||
iecPrefix(lengths[i], "B", bytes, sizeof(bytes)); | |||
vlog.debug(" %s (automatically send up to %s)", | |||
type, bytes); | |||
} | |||
} | |||
} | |||
server.setClipboardCaps(flags, lengths); | |||
} | |||
@@ -143,6 +143,18 @@ void ClientParams::setLEDState(unsigned int state) | |||
ledState_ = state; | |||
} | |||
rdr::U32 ClientParams::clipboardSize(unsigned int format) const | |||
{ | |||
int i; | |||
for (i = 0;i < 16;i++) { | |||
if (((unsigned)1 << i) == format) | |||
return clipSizes[i]; | |||
} | |||
throw Exception("Invalid clipboard format 0x%x", format); | |||
} | |||
void ClientParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | |||
{ | |||
int i, num; |
@@ -85,6 +85,7 @@ namespace rfb { | |||
void setLEDState(unsigned int state); | |||
rdr::U32 clipboardFlags() const { return clipFlags; } | |||
rdr::U32 clipboardSize(unsigned int format) const; | |||
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | |||
// Wrappers to check for functionality rather than specific |
@@ -54,7 +54,8 @@ SConnection::SConnection() | |||
is(0), os(0), reader_(0), writer_(0), ssecurity(0), | |||
authFailureTimer(this, &SConnection::handleAuthFailureTimeout), | |||
state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), | |||
clientClipboard(NULL), hasLocalClipboard(false) | |||
clientClipboard(NULL), hasLocalClipboard(false), | |||
unsolicitedClipboardAttempt(false) | |||
{ | |||
defaultMajorVersion = 3; | |||
defaultMinorVersion = 8; | |||
@@ -370,6 +371,8 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
void SConnection::clientCutText(const char* str) | |||
{ | |||
hasLocalClipboard = false; | |||
strFree(clientClipboard); | |||
clientClipboard = NULL; | |||
@@ -380,19 +383,21 @@ void SConnection::clientCutText(const char* str) | |||
void SConnection::handleClipboardRequest(rdr::U32 flags) | |||
{ | |||
if (!(flags & rfb::clipboardUTF8)) | |||
if (!(flags & rfb::clipboardUTF8)) { | |||
vlog.debug("Ignoring clipboard request for unsupported formats 0x%x", flags); | |||
return; | |||
if (!hasLocalClipboard) | |||
} | |||
if (!hasLocalClipboard) { | |||
vlog.debug("Ignoring unexpected clipboard request"); | |||
return; | |||
} | |||
handleClipboardRequest(); | |||
} | |||
void SConnection::handleClipboardPeek(rdr::U32 flags) | |||
{ | |||
if (!hasLocalClipboard) | |||
return; | |||
if (client.clipboardFlags() & rfb::clipboardNotify) | |||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||
writer()->writeClipboardNotify(hasLocalClipboard ? rfb::clipboardUTF8 : 0); | |||
} | |||
void SConnection::handleClipboardNotify(rdr::U32 flags) | |||
@@ -400,24 +405,29 @@ void SConnection::handleClipboardNotify(rdr::U32 flags) | |||
strFree(clientClipboard); | |||
clientClipboard = NULL; | |||
if (flags & rfb::clipboardUTF8) | |||
if (flags & rfb::clipboardUTF8) { | |||
hasLocalClipboard = false; | |||
handleClipboardAnnounce(true); | |||
else | |||
} else { | |||
handleClipboardAnnounce(false); | |||
} | |||
} | |||
void SConnection::handleClipboardProvide(rdr::U32 flags, | |||
const size_t* lengths, | |||
const rdr::U8* const* data) | |||
{ | |||
if (!(flags & rfb::clipboardUTF8)) | |||
if (!(flags & rfb::clipboardUTF8)) { | |||
vlog.debug("Ignoring clipboard provide with unsupported formats 0x%x", flags); | |||
return; | |||
} | |||
strFree(clientClipboard); | |||
clientClipboard = NULL; | |||
clientClipboard = convertLF((const char*)data[0], lengths[0]); | |||
// FIXME: Should probably verify that this data was actually requested | |||
handleClipboardData(clientClipboard); | |||
} | |||
@@ -547,14 +557,27 @@ void SConnection::requestClipboard() | |||
void SConnection::announceClipboard(bool available) | |||
{ | |||
hasLocalClipboard = available; | |||
unsolicitedClipboardAttempt = false; | |||
if (client.supportsEncoding(pseudoEncodingExtendedClipboard) && | |||
(client.clipboardFlags() & rfb::clipboardNotify)) | |||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); | |||
else { | |||
if (available) | |||
if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) { | |||
// Attempt an unsolicited transfer? | |||
if (available && | |||
(client.clipboardSize(rfb::clipboardUTF8) > 0) && | |||
(client.clipboardFlags() & rfb::clipboardProvide)) { | |||
vlog.debug("Attempting unsolicited clipboard transfer..."); | |||
unsolicitedClipboardAttempt = true; | |||
handleClipboardRequest(); | |||
return; | |||
} | |||
if (client.clipboardFlags() & rfb::clipboardNotify) { | |||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); | |||
return; | |||
} | |||
} | |||
if (available) | |||
handleClipboardRequest(); | |||
} | |||
void SConnection::sendClipboardData(const char* data) | |||
@@ -564,6 +587,17 @@ void SConnection::sendClipboardData(const char* data) | |||
CharArray filtered(convertCRLF(data)); | |||
size_t sizes[1] = { strlen(filtered.buf) + 1 }; | |||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf }; | |||
if (unsolicitedClipboardAttempt) { | |||
unsolicitedClipboardAttempt = false; | |||
if (sizes[0] > client.clipboardSize(rfb::clipboardUTF8)) { | |||
vlog.debug("Clipboard was too large for unsolicited clipboard transfer"); | |||
if (client.clipboardFlags() & rfb::clipboardNotify) | |||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||
return; | |||
} | |||
} | |||
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | |||
} else { | |||
CharArray latin1(utf8ToLatin1(data)); |
@@ -264,6 +264,7 @@ namespace rfb { | |||
char* clientClipboard; | |||
bool hasLocalClipboard; | |||
bool unsolicitedClipboardAttempt; | |||
}; | |||
} | |||
#endif |
@@ -17,12 +17,16 @@ | |||
* USA. | |||
*/ | |||
#include <rfb/Exception.h> | |||
#include <rfb/LogWriter.h> | |||
#include <rfb/SMsgHandler.h> | |||
#include <rfb/ScreenSet.h> | |||
#include <rfb/clipboardTypes.h> | |||
#include <rfb/encodings.h> | |||
using namespace rfb; | |||
static LogWriter vlog("SMsgHandler"); | |||
SMsgHandler::SMsgHandler() | |||
{ | |||
} | |||
@@ -66,6 +70,46 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) | |||
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | |||
{ | |||
int i; | |||
vlog.debug("Got client clipboard capabilities:"); | |||
for (i = 0;i < 16;i++) { | |||
if (flags & (1 << i)) { | |||
const char *type; | |||
switch (1 << i) { | |||
case clipboardUTF8: | |||
type = "Plain text"; | |||
break; | |||
case clipboardRTF: | |||
type = "Rich text"; | |||
break; | |||
case clipboardHTML: | |||
type = "HTML"; | |||
break; | |||
case clipboardDIB: | |||
type = "Images"; | |||
break; | |||
case clipboardFiles: | |||
type = "Files"; | |||
break; | |||
default: | |||
vlog.debug(" Unknown format 0x%x", 1 << i); | |||
continue; | |||
} | |||
if (lengths[i] == 0) | |||
vlog.debug(" %s (only notify)", type); | |||
else { | |||
char bytes[1024]; | |||
iecPrefix(lengths[i], "B", bytes, sizeof(bytes)); | |||
vlog.debug(" %s (automatically send up to %s)", | |||
type, bytes); | |||
} | |||
} | |||
} | |||
client.setClipboardCaps(flags, lengths); | |||
} | |||
@@ -87,6 +87,18 @@ void ServerParams::setLEDState(unsigned int state) | |||
ledState_ = state; | |||
} | |||
rdr::U32 ServerParams::clipboardSize(unsigned int format) const | |||
{ | |||
int i; | |||
for (i = 0;i < 16;i++) { | |||
if (((unsigned)1 << i) == format) | |||
return clipSizes[i]; | |||
} | |||
throw Exception("Invalid clipboard format 0x%x", format); | |||
} | |||
void ServerParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | |||
{ | |||
int i, num; |
@@ -70,6 +70,7 @@ namespace rfb { | |||
void setLEDState(unsigned int state); | |||
rdr::U32 clipboardFlags() const { return clipFlags; } | |||
rdr::U32 clipboardSize(unsigned int format) const; | |||
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | |||
bool supportsQEMUKeyEvent; |
@@ -171,7 +171,7 @@ void VNCServerST::removeSocket(network::Socket* sock) { | |||
if (pointerClient == *ci) | |||
pointerClient = NULL; | |||
if (clipboardClient == *ci) | |||
clipboardClient = NULL; | |||
handleClipboardAnnounce(*ci, false); | |||
clipboardRequestors.remove(*ci); | |||
CharArray name(strDup((*ci)->getPeerEndpoint())); | |||
@@ -517,8 +517,10 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, | |||
void VNCServerST::handleClipboardData(VNCSConnectionST* client, | |||
const char* data) | |||
{ | |||
if (client != clipboardClient) | |||
if (client != clipboardClient) { | |||
slog.debug("Ignoring unexpected clipboard data"); | |||
return; | |||
} | |||
desktop->handleClipboardData(data); | |||
} | |||
@@ -291,12 +291,8 @@ void Viewport::handleClipboardAnnounce(bool available) | |||
if (!acceptClipboard) | |||
return; | |||
if (available) | |||
vlog.debug("Got notification of new clipboard on server"); | |||
else | |||
vlog.debug("Clipboard is no longer available on server"); | |||
if (!available) { | |||
vlog.debug("Clipboard is no longer available on server"); | |||
pendingServerClipboard = false; | |||
return; | |||
} | |||
@@ -304,10 +300,12 @@ void Viewport::handleClipboardAnnounce(bool available) | |||
pendingClientClipboard = false; | |||
if (!hasFocus()) { | |||
vlog.debug("Got notification of new clipboard on server whilst not focused, will request data later"); | |||
pendingServerClipboard = true; | |||
return; | |||
} | |||
vlog.debug("Got notification of new clipboard on server, requesting data"); | |||
cc->requestClipboard(); | |||
} | |||
@@ -761,12 +759,14 @@ void Viewport::handleClipboardChange(int source, void *data) | |||
self->pendingServerClipboard = false; | |||
if (!self->hasFocus()) { | |||
vlog.debug("Local clipboard changed whilst not focused, will notify server later"); | |||
self->pendingClientClipboard = true; | |||
// Clear any older client clipboard from the server | |||
self->cc->announceClipboard(false); | |||
return; | |||
} | |||
vlog.debug("Local clipboard changed, notifying server"); | |||
try { | |||
self->cc->announceClipboard(true); | |||
} catch (rdr::Exception& e) { | |||
@@ -780,6 +780,7 @@ void Viewport::handleClipboardChange(int source, void *data) | |||
void Viewport::flushPendingClipboard() | |||
{ | |||
if (pendingServerClipboard) { | |||
vlog.debug("Focus regained after remote clipboard change, requesting data"); | |||
try { | |||
cc->requestClipboard(); | |||
} catch (rdr::Exception& e) { | |||
@@ -789,6 +790,7 @@ void Viewport::flushPendingClipboard() | |||
} | |||
} | |||
if (pendingClientClipboard) { | |||
vlog.debug("Focus regained after local clipboard change, notifying server"); | |||
try { | |||
cc->announceClipboard(true); | |||
} catch (rdr::Exception& e) { |