void CConnection::handleClipboardRequest(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; | return; | ||||
if (!hasLocalClipboard) | |||||
} | |||||
if (!hasLocalClipboard) { | |||||
vlog.debug("Ignoring unexpected clipboard request"); | |||||
return; | return; | ||||
} | |||||
handleClipboardRequest(); | handleClipboardRequest(); | ||||
} | } | ||||
void CConnection::handleClipboardPeek(rdr::U32 flags) | void CConnection::handleClipboardPeek(rdr::U32 flags) | ||||
{ | { | ||||
if (!hasLocalClipboard) | |||||
return; | |||||
if (server.clipboardFlags() & rfb::clipboardNotify) | if (server.clipboardFlags() & rfb::clipboardNotify) | ||||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||||
writer()->writeClipboardNotify(hasLocalClipboard ? rfb::clipboardUTF8 : 0); | |||||
} | } | ||||
void CConnection::handleClipboardNotify(rdr::U32 flags) | void CConnection::handleClipboardNotify(rdr::U32 flags) | ||||
const size_t* lengths, | const size_t* lengths, | ||||
const rdr::U8* const* data) | 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; | return; | ||||
} | |||||
strFree(serverClipboard); | strFree(serverClipboard); | ||||
serverClipboard = NULL; | serverClipboard = NULL; | ||||
void CConnection::announceClipboard(bool available) | void CConnection::announceClipboard(bool available) | ||||
{ | { | ||||
hasLocalClipboard = 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); | writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); | ||||
else { | |||||
if (available) | |||||
handleClipboardRequest(); | |||||
return; | |||||
} | } | ||||
if (available) | |||||
handleClipboardRequest(); | |||||
} | } | ||||
void CConnection::sendClipboardData(const char* data) | void CConnection::sendClipboardData(const char* data) | ||||
CharArray filtered(convertCRLF(data)); | CharArray filtered(convertCRLF(data)); | ||||
size_t sizes[1] = { strlen(filtered.buf) + 1 }; | size_t sizes[1] = { strlen(filtered.buf) + 1 }; | ||||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf }; | 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); | writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | ||||
} else { | } else { | ||||
CharArray latin1(utf8ToLatin1(data)); | CharArray latin1(utf8ToLatin1(data)); |
char* serverClipboard; | char* serverClipboard; | ||||
bool hasLocalClipboard; | bool hasLocalClipboard; | ||||
bool unsolicitedClipboardAttempt; | |||||
}; | }; | ||||
} | } | ||||
#endif | #endif |
#include <rfb/Exception.h> | #include <rfb/Exception.h> | ||||
#include <rfb/LogWriter.h> | #include <rfb/LogWriter.h> | ||||
#include <rfb/CMsgHandler.h> | #include <rfb/CMsgHandler.h> | ||||
#include <rfb/clipboardTypes.h> | |||||
#include <rfb/screenTypes.h> | #include <rfb/screenTypes.h> | ||||
static rfb::LogWriter vlog("CMsgHandler"); | static rfb::LogWriter vlog("CMsgHandler"); | ||||
void CMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | 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); | server.setClipboardCaps(flags, lengths); | ||||
} | } | ||||
ledState_ = 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) | void ClientParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | ||||
{ | { | ||||
int i, num; | int i, num; |
void setLEDState(unsigned int state); | void setLEDState(unsigned int state); | ||||
rdr::U32 clipboardFlags() const { return clipFlags; } | rdr::U32 clipboardFlags() const { return clipFlags; } | ||||
rdr::U32 clipboardSize(unsigned int format) const; | |||||
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | ||||
// Wrappers to check for functionality rather than specific | // Wrappers to check for functionality rather than specific |
is(0), os(0), reader_(0), writer_(0), ssecurity(0), | is(0), os(0), reader_(0), writer_(0), ssecurity(0), | ||||
authFailureTimer(this, &SConnection::handleAuthFailureTimeout), | authFailureTimer(this, &SConnection::handleAuthFailureTimeout), | ||||
state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), | state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), | ||||
clientClipboard(NULL), hasLocalClipboard(false) | |||||
clientClipboard(NULL), hasLocalClipboard(false), | |||||
unsolicitedClipboardAttempt(false) | |||||
{ | { | ||||
defaultMajorVersion = 3; | defaultMajorVersion = 3; | ||||
defaultMinorVersion = 8; | defaultMinorVersion = 8; | ||||
void SConnection::clientCutText(const char* str) | void SConnection::clientCutText(const char* str) | ||||
{ | { | ||||
hasLocalClipboard = false; | |||||
strFree(clientClipboard); | strFree(clientClipboard); | ||||
clientClipboard = NULL; | clientClipboard = NULL; | ||||
void SConnection::handleClipboardRequest(rdr::U32 flags) | 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; | return; | ||||
if (!hasLocalClipboard) | |||||
} | |||||
if (!hasLocalClipboard) { | |||||
vlog.debug("Ignoring unexpected clipboard request"); | |||||
return; | return; | ||||
} | |||||
handleClipboardRequest(); | handleClipboardRequest(); | ||||
} | } | ||||
void SConnection::handleClipboardPeek(rdr::U32 flags) | void SConnection::handleClipboardPeek(rdr::U32 flags) | ||||
{ | { | ||||
if (!hasLocalClipboard) | |||||
return; | |||||
if (client.clipboardFlags() & rfb::clipboardNotify) | if (client.clipboardFlags() & rfb::clipboardNotify) | ||||
writer()->writeClipboardNotify(rfb::clipboardUTF8); | |||||
writer()->writeClipboardNotify(hasLocalClipboard ? rfb::clipboardUTF8 : 0); | |||||
} | } | ||||
void SConnection::handleClipboardNotify(rdr::U32 flags) | void SConnection::handleClipboardNotify(rdr::U32 flags) | ||||
strFree(clientClipboard); | strFree(clientClipboard); | ||||
clientClipboard = NULL; | clientClipboard = NULL; | ||||
if (flags & rfb::clipboardUTF8) | |||||
if (flags & rfb::clipboardUTF8) { | |||||
hasLocalClipboard = false; | |||||
handleClipboardAnnounce(true); | handleClipboardAnnounce(true); | ||||
else | |||||
} else { | |||||
handleClipboardAnnounce(false); | handleClipboardAnnounce(false); | ||||
} | |||||
} | } | ||||
void SConnection::handleClipboardProvide(rdr::U32 flags, | void SConnection::handleClipboardProvide(rdr::U32 flags, | ||||
const size_t* lengths, | const size_t* lengths, | ||||
const rdr::U8* const* data) | 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; | return; | ||||
} | |||||
strFree(clientClipboard); | strFree(clientClipboard); | ||||
clientClipboard = NULL; | clientClipboard = NULL; | ||||
clientClipboard = convertLF((const char*)data[0], lengths[0]); | clientClipboard = convertLF((const char*)data[0], lengths[0]); | ||||
// FIXME: Should probably verify that this data was actually requested | |||||
handleClipboardData(clientClipboard); | handleClipboardData(clientClipboard); | ||||
} | } | ||||
void SConnection::announceClipboard(bool available) | void SConnection::announceClipboard(bool available) | ||||
{ | { | ||||
hasLocalClipboard = 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(); | handleClipboardRequest(); | ||||
return; | |||||
} | |||||
if (client.clipboardFlags() & rfb::clipboardNotify) { | |||||
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); | |||||
return; | |||||
} | |||||
} | } | ||||
if (available) | |||||
handleClipboardRequest(); | |||||
} | } | ||||
void SConnection::sendClipboardData(const char* data) | void SConnection::sendClipboardData(const char* data) | ||||
CharArray filtered(convertCRLF(data)); | CharArray filtered(convertCRLF(data)); | ||||
size_t sizes[1] = { strlen(filtered.buf) + 1 }; | size_t sizes[1] = { strlen(filtered.buf) + 1 }; | ||||
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf }; | 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); | writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); | ||||
} else { | } else { | ||||
CharArray latin1(utf8ToLatin1(data)); | CharArray latin1(utf8ToLatin1(data)); |
char* clientClipboard; | char* clientClipboard; | ||||
bool hasLocalClipboard; | bool hasLocalClipboard; | ||||
bool unsolicitedClipboardAttempt; | |||||
}; | }; | ||||
} | } | ||||
#endif | #endif |
* USA. | * USA. | ||||
*/ | */ | ||||
#include <rfb/Exception.h> | #include <rfb/Exception.h> | ||||
#include <rfb/LogWriter.h> | |||||
#include <rfb/SMsgHandler.h> | #include <rfb/SMsgHandler.h> | ||||
#include <rfb/ScreenSet.h> | #include <rfb/ScreenSet.h> | ||||
#include <rfb/clipboardTypes.h> | |||||
#include <rfb/encodings.h> | #include <rfb/encodings.h> | ||||
using namespace rfb; | using namespace rfb; | ||||
static LogWriter vlog("SMsgHandler"); | |||||
SMsgHandler::SMsgHandler() | SMsgHandler::SMsgHandler() | ||||
{ | { | ||||
} | } | ||||
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | 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); | client.setClipboardCaps(flags, lengths); | ||||
} | } | ||||
ledState_ = 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) | void ServerParams::setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) | ||||
{ | { | ||||
int i, num; | int i, num; |
void setLEDState(unsigned int state); | void setLEDState(unsigned int state); | ||||
rdr::U32 clipboardFlags() const { return clipFlags; } | rdr::U32 clipboardFlags() const { return clipFlags; } | ||||
rdr::U32 clipboardSize(unsigned int format) const; | |||||
void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | void setClipboardCaps(rdr::U32 flags, const rdr::U32* lengths); | ||||
bool supportsQEMUKeyEvent; | bool supportsQEMUKeyEvent; |
if (pointerClient == *ci) | if (pointerClient == *ci) | ||||
pointerClient = NULL; | pointerClient = NULL; | ||||
if (clipboardClient == *ci) | if (clipboardClient == *ci) | ||||
clipboardClient = NULL; | |||||
handleClipboardAnnounce(*ci, false); | |||||
clipboardRequestors.remove(*ci); | clipboardRequestors.remove(*ci); | ||||
CharArray name(strDup((*ci)->getPeerEndpoint())); | CharArray name(strDup((*ci)->getPeerEndpoint())); | ||||
void VNCServerST::handleClipboardData(VNCSConnectionST* client, | void VNCServerST::handleClipboardData(VNCSConnectionST* client, | ||||
const char* data) | const char* data) | ||||
{ | { | ||||
if (client != clipboardClient) | |||||
if (client != clipboardClient) { | |||||
slog.debug("Ignoring unexpected clipboard data"); | |||||
return; | return; | ||||
} | |||||
desktop->handleClipboardData(data); | desktop->handleClipboardData(data); | ||||
} | } | ||||
if (!acceptClipboard) | if (!acceptClipboard) | ||||
return; | return; | ||||
if (available) | |||||
vlog.debug("Got notification of new clipboard on server"); | |||||
else | |||||
vlog.debug("Clipboard is no longer available on server"); | |||||
if (!available) { | if (!available) { | ||||
vlog.debug("Clipboard is no longer available on server"); | |||||
pendingServerClipboard = false; | pendingServerClipboard = false; | ||||
return; | return; | ||||
} | } | ||||
pendingClientClipboard = false; | pendingClientClipboard = false; | ||||
if (!hasFocus()) { | if (!hasFocus()) { | ||||
vlog.debug("Got notification of new clipboard on server whilst not focused, will request data later"); | |||||
pendingServerClipboard = true; | pendingServerClipboard = true; | ||||
return; | return; | ||||
} | } | ||||
vlog.debug("Got notification of new clipboard on server, requesting data"); | |||||
cc->requestClipboard(); | cc->requestClipboard(); | ||||
} | } | ||||
self->pendingServerClipboard = false; | self->pendingServerClipboard = false; | ||||
if (!self->hasFocus()) { | if (!self->hasFocus()) { | ||||
vlog.debug("Local clipboard changed whilst not focused, will notify server later"); | |||||
self->pendingClientClipboard = true; | self->pendingClientClipboard = true; | ||||
// Clear any older client clipboard from the server | // Clear any older client clipboard from the server | ||||
self->cc->announceClipboard(false); | self->cc->announceClipboard(false); | ||||
return; | return; | ||||
} | } | ||||
vlog.debug("Local clipboard changed, notifying server"); | |||||
try { | try { | ||||
self->cc->announceClipboard(true); | self->cc->announceClipboard(true); | ||||
} catch (rdr::Exception& e) { | } catch (rdr::Exception& e) { | ||||
void Viewport::flushPendingClipboard() | void Viewport::flushPendingClipboard() | ||||
{ | { | ||||
if (pendingServerClipboard) { | if (pendingServerClipboard) { | ||||
vlog.debug("Focus regained after remote clipboard change, requesting data"); | |||||
try { | try { | ||||
cc->requestClipboard(); | cc->requestClipboard(); | ||||
} catch (rdr::Exception& e) { | } catch (rdr::Exception& e) { | ||||
} | } | ||||
} | } | ||||
if (pendingClientClipboard) { | if (pendingClientClipboard) { | ||||
vlog.debug("Focus regained after local clipboard change, notifying server"); | |||||
try { | try { | ||||
cc->announceClipboard(true); | cc->announceClipboard(true); | ||||
} catch (rdr::Exception& e) { | } catch (rdr::Exception& e) { |