Chromeclip (#69)

* Initial binary clipboard support

* Rename -DLP_Clip_Types to -DLP_ClipTypes

* Better handling of websocket frames

* Copy-paste bug in SSE2 scaling to under 0.5x

* Remove old text clipboard

* Bind text to binary clipboard

* Move binclip clear to probing phase

* Off-by-one in sse2 scaling

* Add a clarifying log message for INCR clipboard transfers

* WIP: Update novnc commit

* Fix CentOS pipeline

* webpack fix

* Update novnc commit

* Change some DLP defaults

* update novnc commit

Co-authored-by: Lauri Kasanen <cand@gmx.com>
Co-authored-by: matt <matt@kasmweb.com>
This commit is contained in:
mmcclaskey 2021-10-15 15:57:58 -04:00 committed by GitHub
parent e6d1b8c3c8
commit 5b28a168d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 524 additions and 603 deletions

View File

@ -284,74 +284,26 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
} }
SMsgHandler::setEncodings(nEncodings, encodings); SMsgHandler::setEncodings(nEncodings, encodings);
if (cp.supportsExtendedClipboard) {
rdr::U32 sizes[] = { 0 };
writer()->writeClipboardCaps(rfb::clipboardUTF8 |
rfb::clipboardRequest |
rfb::clipboardPeek |
rfb::clipboardNotify |
rfb::clipboardProvide,
sizes);
}
} }
void SConnection::clientCutText(const char* str, int len) void SConnection::clearBinaryClipboard()
{ {
hasLocalClipboard = false; binaryClipboard.clear();
strFree(clientClipboard);
clientClipboard = NULL;
clientClipboard = latin1ToUTF8(str);
handleClipboardAnnounce(true);
} }
void SConnection::handleClipboardRequest(rdr::U32 flags) void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len)
{ {
if (!(flags & rfb::clipboardUTF8)) binaryClipboard_t bin;
return; strncpy(bin.mime, mime, sizeof(bin.mime));
if (!hasLocalClipboard) bin.mime[sizeof(bin.mime) - 1] = '\0';
return;
handleClipboardRequest(); bin.data.resize(len);
memcpy(&bin.data[0], data, len);
binaryClipboard.push_back(bin);
} }
void SConnection::handleClipboardPeek(rdr::U32 flags)
{
if (!hasLocalClipboard)
return;
if (cp.clipboardFlags() & rfb::clipboardNotify)
writer()->writeClipboardNotify(rfb::clipboardUTF8);
}
void SConnection::handleClipboardNotify(rdr::U32 flags)
{
strFree(clientClipboard);
clientClipboard = NULL;
if (flags & rfb::clipboardUTF8) {
handleClipboardAnnounce(true);
hasLocalClipboard = false;
} else {
handleClipboardAnnounce(false);
}
}
void SConnection::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
if (!(flags & rfb::clipboardUTF8))
return;
strFree(clientClipboard);
clientClipboard = NULL;
clientClipboard = convertLF((const char*)data[0], lengths[0]);
handleClipboardData(clientClipboard, strlen(clientClipboard));
}
void SConnection::supportsQEMUKeyEvent() void SConnection::supportsQEMUKeyEvent()
{ {
@ -445,56 +397,13 @@ void SConnection::enableContinuousUpdates(bool enable,
{ {
} }
void SConnection::handleClipboardRequest()
{
}
void SConnection::handleClipboardAnnounce(bool available) void SConnection::handleClipboardAnnounce(bool available)
{ {
} }
void SConnection::handleClipboardData(const char* data, int len)
{
}
void SConnection::requestClipboard()
{
if (clientClipboard != NULL) {
handleClipboardData(clientClipboard, strlen(clientClipboard));
return;
}
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardRequest))
writer()->writeClipboardRequest(rfb::clipboardUTF8);
}
void SConnection::announceClipboard(bool available) void SConnection::announceClipboard(bool available)
{ {
hasLocalClipboard = available; hasLocalClipboard = available;
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardNotify))
writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
else {
if (available)
handleClipboardRequest();
}
}
void SConnection::sendClipboardData(const char* data, int len)
{
if (cp.supportsExtendedClipboard &&
(cp.clipboardFlags() & rfb::clipboardProvide)) {
CharArray filtered(convertCRLF(data));
size_t sizes[1] = { strlen(filtered.buf) + 1 };
const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
} else {
CharArray latin1(utf8ToLatin1(data));
writer()->writeServerCutText(latin1.buf, strlen(latin1.buf));
}
} }
void SConnection::writeFakeColourMap(void) void SConnection::writeFakeColourMap(void)

View File

@ -28,6 +28,7 @@
#include <rdr/OutStream.h> #include <rdr/OutStream.h>
#include <rfb/SMsgHandler.h> #include <rfb/SMsgHandler.h>
#include <rfb/SecurityServer.h> #include <rfb/SecurityServer.h>
#include <vector>
namespace rfb { namespace rfb {
@ -73,14 +74,9 @@ namespace rfb {
virtual void setEncodings(int nEncodings, const rdr::S32* encodings); virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
virtual void clientCutText(const char* str, int len); virtual void clearBinaryClipboard();
virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
virtual void handleClipboardRequest(rdr::U32 flags); const rdr::U32 len);
virtual void handleClipboardPeek(rdr::U32 flags);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
virtual void supportsQEMUKeyEvent(); virtual void supportsQEMUKeyEvent();
@ -127,25 +123,11 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable, virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h); int x, int y, int w, int h);
// handleClipboardRequest() is called whenever the client requests
// the server to send over its clipboard data. It will only be
// called after the server has first announced a clipboard change
// via announceClipboard().
virtual void handleClipboardRequest();
// handleClipboardAnnounce() is called to indicate a change in the // handleClipboardAnnounce() is called to indicate a change in the
// clipboard on the client. Call requestClipboard() to access the // clipboard on the client. Call requestClipboard() to access the
// actual data. // actual data.
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
// handleClipboardData() is called when the client has sent over
// the clipboard data as a result of a previous call to
// requestClipboard(). Note that this function might never be
// called if the clipboard data was no longer available when the
// client received the request.
virtual void handleClipboardData(const char* data, int len);
virtual void add_changed_all() {} virtual void add_changed_all() {}
// setAccessRights() allows a security package to limit the access rights // setAccessRights() allows a security package to limit the access rights
@ -166,22 +148,11 @@ namespace rfb {
// Other methods // Other methods
// requestClipboard() will result in a request to the client to
// transfer its clipboard data. A call to handleClipboardData()
// will be made once the data is available.
virtual void requestClipboard();
// announceClipboard() informs the client of changes to the // announceClipboard() informs the client of changes to the
// clipboard on the server. The client may later request the // clipboard on the server. The client may later request the
// clipboard data via handleClipboardRequest(). // clipboard data via handleClipboardRequest().
virtual void announceClipboard(bool available); virtual void announceClipboard(bool available);
// sendClipboardData() transfers the clipboard data to the client
// and should be called whenever the client has requested the
// clipboard via handleClipboardRequest().
virtual void sendClipboardData(const char* data, int len);
// authenticated() returns true if the client has authenticated // authenticated() returns true if the client has authenticated
// successfully. // successfully.
bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
@ -221,12 +192,19 @@ namespace rfb {
rdr::S32 getPreferredEncoding() { return preferredEncoding; } rdr::S32 getPreferredEncoding() { return preferredEncoding; }
struct binaryClipboard_t {
char mime[32];
std::vector<unsigned char> data;
};
protected: protected:
void setState(stateEnum s) { state_ = s; } void setState(stateEnum s) { state_ = s; }
void setReader(SMsgReader *r) { reader_ = r; } void setReader(SMsgReader *r) { reader_ = r; }
void setWriter(SMsgWriter *w) { writer_ = w; } void setWriter(SMsgWriter *w) { writer_ = w; }
std::vector<binaryClipboard_t> binaryClipboard;
private: private:
void writeFakeColourMap(void); void writeFakeColourMap(void);

View File

@ -78,23 +78,13 @@ namespace rfb {
// the relevant RFB protocol messages from clients. // the relevant RFB protocol messages from clients.
// See InputHandler for method signatures. // See InputHandler for method signatures.
// handleClipboardRequest() is called whenever a client requests
// the server to send over its clipboard data. It will only be
// called after the server has first announced a clipboard change
// via VNCServer::announceClipboard().
virtual void handleClipboardRequest() {}
// handleClipboardAnnounce() is called to indicate a change in the // handleClipboardAnnounce() is called to indicate a change in the
// clipboard on a client. Call VNCServer::requestClipboard() to // clipboard on a client. Call VNCServer::requestClipboard() to
// access the actual data. // access the actual data.
virtual void handleClipboardAnnounce(bool __unused_attr available) {} virtual void handleClipboardAnnounce(bool __unused_attr available) {}
// handleClipboardData() is called when a client has sent over virtual void handleClipboardAnnounceBinary(const unsigned __unused_attr num,
// the clipboard data as a result of a previous call to const char __unused_attr mimes[][32]) {}
// VNCServer::requestClipboard(). Note that this function might
// never be called if the clipboard data was no longer available
// when the client received the request.
virtual void handleClipboardData(const char* __unused_attr data, int len __unused_attr) {}
protected: protected:
virtual ~SDesktop() {} virtual ~SDesktop() {}

View File

@ -64,26 +64,16 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsQEMUKeyEvent(); supportsQEMUKeyEvent();
} }
void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32])
{
cp.setClipboardCaps(flags, lengths);
}
void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
{ {
} }
void SMsgHandler::handleClipboardPeek(rdr::U32 flags) void SMsgHandler::clearBinaryClipboard()
{ {
} }
void SMsgHandler::handleClipboardNotify(rdr::U32 flags) void SMsgHandler::addBinaryClipboard(const char mime[], const rdr::U8 *data,
{ const rdr::U32 len)
}
void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{ {
} }

View File

@ -54,14 +54,10 @@ namespace rfb {
virtual void enableContinuousUpdates(bool enable, virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h) = 0; int x, int y, int w, int h) = 0;
virtual void handleClipboardCaps(rdr::U32 flags, virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
const rdr::U32* lengths); virtual void clearBinaryClipboard();
virtual void handleClipboardRequest(rdr::U32 flags); virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
virtual void handleClipboardPeek(rdr::U32 flags); const rdr::U32 len);
virtual void handleClipboardNotify(rdr::U32 flags);
virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data);
virtual void sendStats(const bool toClient = true) = 0; virtual void sendStats(const bool toClient = true) = 0;
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0; virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;

View File

@ -35,8 +35,6 @@ using namespace rfb;
static LogWriter vlog("SMsgReader"); static LogWriter vlog("SMsgReader");
static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_) SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
: handler(handler_), is(is_) : handler(handler_), is(is_)
{ {
@ -83,6 +81,9 @@ void SMsgReader::readMsg()
case msgTypeFrameStats: case msgTypeFrameStats:
readFrameStats(); readFrameStats();
break; break;
case msgTypeBinaryClipboard:
readBinaryClipboard();
break;
case msgTypeKeyEvent: case msgTypeKeyEvent:
readKeyEvent(); readKeyEvent();
break; break;
@ -240,109 +241,54 @@ void SMsgReader::readClientCutText()
readExtendedClipboard(slen); readExtendedClipboard(slen);
return; return;
} }
if (len > (size_t)maxCutText) {
is->skip(len); is->skip(len);
vlog.error("Cut text too long (%d bytes) - ignoring", len); vlog.error("Client sent old cuttext msg, ignoring");
return; }
void SMsgReader::readBinaryClipboard()
{
const rdr::U8 num = is->readU8();
rdr::U8 i, valid = 0;
char tmpmimes[num][32];
handler->clearBinaryClipboard();
for (i = 0; i < num; i++) {
const rdr::U8 mimelen = is->readU8();
if (mimelen > 32 - 1) {
vlog.error("Mime too long (%u)", mimelen);
} }
CharArray ca(len+1);
ca.buf[len] = 0; char mime[mimelen + 1];
mime[mimelen] = '\0';
is->readBytes(mime, mimelen);
strncpy(tmpmimes[valid], mime, 32);
tmpmimes[valid][31] = '\0';
const rdr::U32 len = is->readU32();
CharArray ca(len);
is->readBytes(ca.buf, len); is->readBytes(ca.buf, len);
handler->clientCutText(ca.buf, len);
if (rfb::Server::DLP_ClipAcceptMax && len > (unsigned) rfb::Server::DLP_ClipAcceptMax) {
vlog.info("DLP: refused to receive binary clipboard, too large");
continue;
}
vlog.debug("Received binary clipboard, type %s, %u bytes", mime, len);
handler->addBinaryClipboard(mime, (rdr::U8 *) ca.buf, len);
valid++;
}
handler->handleClipboardAnnounceBinary(valid, tmpmimes);
} }
void SMsgReader::readExtendedClipboard(rdr::S32 len) void SMsgReader::readExtendedClipboard(rdr::S32 len)
{ {
rdr::U32 flags;
rdr::U32 action;
if (len < 4) if (len < 4)
throw Exception("Invalid extended clipboard message"); throw Exception("Invalid extended clipboard message");
if (len > maxCutText) { vlog.error("Client sent old cuttext msg, ignoring");
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
is->skip(len); is->skip(len);
return;
}
flags = is->readU32();
action = flags & clipboardActionMask;
if (action & clipboardCaps) {
int i;
size_t num;
rdr::U32 lengths[16];
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
num++;
}
if (len < (rdr::S32)(4 + 4*num))
throw Exception("Invalid extended clipboard message");
num = 0;
for (i = 0;i < 16;i++) {
if (flags & (1 << i))
lengths[num++] = is->readU32();
}
handler->handleClipboardCaps(flags, lengths);
} else if (action == clipboardProvide) {
rdr::ZlibInStream zis;
int i;
size_t num;
size_t lengths[16];
rdr::U8* buffers[16];
zis.setUnderlying(is, len - 4);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
lengths[num] = zis.readU32();
if (lengths[num] > (size_t)maxCutText) {
vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
(unsigned)lengths[num]);
zis.skip(lengths[num]);
flags &= ~(1 << i);
continue;
}
buffers[num] = new rdr::U8[lengths[num]];
zis.readBytes(buffers[num], lengths[num]);
num++;
}
zis.flushUnderlying();
zis.setUnderlying(NULL, 0);
handler->handleClipboardProvide(flags, lengths, buffers);
num = 0;
for (i = 0;i < 16;i++) {
if (!(flags & 1 << i))
continue;
delete [] buffers[num++];
}
} else {
switch (action) {
case clipboardRequest:
handler->handleClipboardRequest(flags);
break;
case clipboardPeek:
handler->handleClipboardPeek(flags);
break;
case clipboardNotify:
handler->handleClipboardNotify(flags);
break;
default:
throw Exception("Invalid extended clipboard action");
}
}
} }
void SMsgReader::readRequestStats() void SMsgReader::readRequestStats()

View File

@ -58,6 +58,7 @@ namespace rfb {
void readExtendedClipboard(rdr::S32 len); void readExtendedClipboard(rdr::S32 len);
void readRequestStats(); void readRequestStats();
void readFrameStats(); void readFrameStats();
void readBinaryClipboard();
void readQEMUMessage(); void readQEMUMessage();
void readQEMUKeyEvent(); void readQEMUKeyEvent();

View File

@ -85,118 +85,21 @@ void SMsgWriter::writeBell()
endMsg(); endMsg();
} }
void SMsgWriter::writeServerCutText(const char* str, int len) void SMsgWriter::writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b)
{ {
startMsg(msgTypeServerCutText); startMsg(msgTypeBinaryClipboard);
os->pad(3);
os->writeU32(len);
os->writeBytes(str, len);
endMsg();
}
void SMsgWriter::writeClipboardCaps(rdr::U32 caps, os->writeU8(b.size());
const rdr::U32* lengths) rdr::U8 i;
{ for (i = 0; i < b.size(); i++) {
size_t i, count; const rdr::U8 mimelen = strlen(b[i].mime);
os->writeU8(mimelen);
os->writeBytes(b[i].mime, mimelen);
if (!cp->supportsExtendedClipboard) os->writeU32(b[i].data.size());
throw Exception("Client does not support extended clipboard"); os->writeBytes(&b[i].data[0], b[i].data.size());
count = 0;
for (i = 0;i < 16;i++) {
if (caps & (1 << i))
count++;
} }
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-(4 + 4 * count));
os->writeU32(caps | clipboardCaps);
count = 0;
for (i = 0;i < 16;i++) {
if (caps & (1 << i))
os->writeU32(lengths[count++]);
}
endMsg();
}
void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardRequest))
throw Exception("Client does not support clipboard \"request\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardRequest);
endMsg();
}
void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardPeek))
throw Exception("Client does not support clipboard \"peek\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardPeek);
endMsg();
}
void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
{
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardNotify))
throw Exception("Client does not support clipboard \"notify\" action");
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-4);
os->writeU32(flags | clipboardNotify);
endMsg();
}
void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
const size_t* lengths,
const rdr::U8* const* data)
{
rdr::MemOutStream mos;
rdr::ZlibOutStream zos;
int i, count;
if (!cp->supportsExtendedClipboard)
throw Exception("Client does not support extended clipboard");
if (!(cp->clipboardFlags() & clipboardProvide))
throw Exception("Client does not support clipboard \"provide\" action");
zos.setUnderlying(&mos);
count = 0;
for (i = 0;i < 16;i++) {
if (!(flags & (1 << i)))
continue;
zos.writeU32(lengths[count]);
zos.writeBytes(data[count], lengths[count]);
count++;
}
zos.flush();
startMsg(msgTypeServerCutText);
os->pad(3);
os->writeS32(-(4 + mos.length()));
os->writeU32(flags | clipboardProvide);
os->writeBytes(mos.data(), mos.length());
endMsg(); endMsg();
} }

View File

@ -26,6 +26,8 @@
#include <rdr/types.h> #include <rdr/types.h>
#include <rfb/encodings.h> #include <rfb/encodings.h>
#include <rfb/ScreenSet.h> #include <rfb/ScreenSet.h>
#include <rfb/SConnection.h>
#include <vector>
namespace rdr { class OutStream; } namespace rdr { class OutStream; }
@ -54,14 +56,8 @@ namespace rfb {
// writeBell() and writeServerCutText() do the obvious thing. // writeBell() and writeServerCutText() do the obvious thing.
void writeBell(); void writeBell();
void writeServerCutText(const char* str, int len);
void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths); void writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b);
void writeClipboardRequest(rdr::U32 flags);
void writeClipboardPeek(rdr::U32 flags);
void writeClipboardNotify(rdr::U32 flags);
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
const rdr::U8* const* data);
void writeStats(const char* str, int len); void writeStats(const char* str, int len);

View File

@ -149,15 +149,15 @@ rfb::IntParameter rfb::Server::webpVideoQuality
rfb::IntParameter rfb::Server::DLP_ClipSendMax rfb::IntParameter rfb::Server::DLP_ClipSendMax
("DLP_ClipSendMax", ("DLP_ClipSendMax",
"Limit clipboard bytes to send to clients in one transaction", "Limit clipboard bytes to send to clients in one transaction",
10000, 0, INT_MAX); 0, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_ClipAcceptMax rfb::IntParameter rfb::Server::DLP_ClipAcceptMax
("DLP_ClipAcceptMax", ("DLP_ClipAcceptMax",
"Limit clipboard bytes to receive from clients in one transaction", "Limit clipboard bytes to receive from clients in one transaction",
10000, 0, INT_MAX); 0, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_ClipDelay rfb::IntParameter rfb::Server::DLP_ClipDelay
("DLP_ClipDelay", ("DLP_ClipDelay",
"This many milliseconds must pass between clipboard actions", "This many milliseconds must pass between clipboard actions",
1000, 0, INT_MAX); 0, 0, INT_MAX);
rfb::IntParameter rfb::Server::DLP_KeyRateLimit rfb::IntParameter rfb::Server::DLP_KeyRateLimit
("DLP_KeyRateLimit", ("DLP_KeyRateLimit",
"Reject keyboard presses over this many per second", "Reject keyboard presses over this many per second",
@ -171,6 +171,10 @@ rfb::StringParameter rfb::Server::DLP_Region
("DLP_Region", ("DLP_Region",
"Black out anything outside this region", "Black out anything outside this region",
""); "");
rfb::StringParameter rfb::Server::DLP_Clip_Types
("DLP_ClipTypes",
"Allowed binary clipboard mimetypes",
"text/html,image/png");
rfb::BoolParameter rfb::Server::DLP_RegionAllowClick rfb::BoolParameter rfb::Server::DLP_RegionAllowClick
("DLP_RegionAllowClick", ("DLP_RegionAllowClick",

View File

@ -50,6 +50,7 @@ namespace rfb {
static IntParameter DLP_KeyRateLimit; static IntParameter DLP_KeyRateLimit;
static StringParameter DLP_ClipLog; static StringParameter DLP_ClipLog;
static StringParameter DLP_Region; static StringParameter DLP_Region;
static StringParameter DLP_Clip_Types;
static BoolParameter DLP_RegionAllowClick; static BoolParameter DLP_RegionAllowClick;
static BoolParameter DLP_RegionAllowRelease; static BoolParameter DLP_RegionAllowRelease;
static IntParameter jpegVideoQuality; static IntParameter jpegVideoQuality;

View File

@ -57,7 +57,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
inProcessMessages(false), inProcessMessages(false),
pendingSyncFence(false), syncFence(false), fenceFlags(0), pendingSyncFence(false), syncFence(false), fenceFlags(0),
fenceDataLen(0), fenceData(NULL), congestionTimer(this), fenceDataLen(0), fenceData(NULL), congestionTimer(this),
losslessTimer(this), kbdLogTimer(this), server(server_), updates(false), losslessTimer(this), kbdLogTimer(this), binclipTimer(this),
server(server_), updates(false),
updateRenderedCursor(false), removeRenderedCursor(false), updateRenderedCursor(false), removeRenderedCursor(false),
continuousUpdates(false), encodeManager(this, &server_->encCache), continuousUpdates(false), encodeManager(this, &server_->encCache),
needsPermCheck(false), pointerEventTime(0), needsPermCheck(false), pointerEventTime(0),
@ -413,18 +414,6 @@ static void keylog(unsigned keysym, const char *client) {
flushKeylog(client); flushKeylog(client);
} }
void VNCSConnectionST::requestClipboardOrClose()
{
try {
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::acceptCutText) return;
if (state() != RFBSTATE_NORMAL) return;
requestClipboard();
} catch(rdr::Exception& e) {
close(e.str());
}
}
void VNCSConnectionST::announceClipboardOrClose(bool available) void VNCSConnectionST::announceClipboardOrClose(bool available)
{ {
try { try {
@ -437,29 +426,51 @@ void VNCSConnectionST::announceClipboardOrClose(bool available)
} }
} }
void VNCSConnectionST::sendClipboardDataOrClose(const char* data) void VNCSConnectionST::clearBinaryClipboardData()
{
clearBinaryClipboard();
}
void VNCSConnectionST::sendBinaryClipboardDataOrClose(const char* mime,
const unsigned char *data,
const unsigned len)
{ {
try { try {
if (!(accessRights & AccessCutText)) return; if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::sendCutText) return; if (!rfb::Server::sendCutText) return;
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { if (rfb::Server::DLP_ClipSendMax && len > (unsigned) rfb::Server::DLP_ClipSendMax) {
vlog.info("DLP: client %s: refused to send clipboard, too soon", vlog.info("DLP: client %s: refused to send binary clipboard, too large",
sock->getPeerAddress()); sock->getPeerAddress());
return; return;
} }
int len = strlen(data);
const int origlen = len; cliplog((const char *) data, len, len, "sent", sock->getPeerAddress());
if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax)
len = rfb::Server::DLP_ClipSendMax;
cliplog(data, len, origlen, "sent", sock->getPeerAddress());
if (state() != RFBSTATE_NORMAL) return; if (state() != RFBSTATE_NORMAL) return;
sendClipboardData(data, len);
gettimeofday(&lastClipboardOp, NULL); addBinaryClipboard(mime, data, len);
binclipTimer.start(100);
} catch(rdr::Exception& e) { } catch(rdr::Exception& e) {
close(e.str()); close(e.str());
} }
} }
void VNCSConnectionST::getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len)
{
unsigned i;
for (i = 0; i < binaryClipboard.size(); i++) {
if (!strcmp(binaryClipboard[i].mime, mime)) {
*data = &binaryClipboard[i].data[0];
*len = binaryClipboard[i].data.size();
return;
}
}
vlog.error("Binary clipboard data for mime %s not found", mime);
*data = (const unsigned char *) "";
*len = 1;
}
void VNCSConnectionST::setDesktopNameOrClose(const char *name) void VNCSConnectionST::setDesktopNameOrClose(const char *name)
{ {
try { try {
@ -1014,12 +1025,6 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable,
} }
} }
void VNCSConnectionST::handleClipboardRequest()
{
if (!(accessRights & AccessCutText)) return;
server->handleClipboardRequest(this);
}
void VNCSConnectionST::handleClipboardAnnounce(bool available) void VNCSConnectionST::handleClipboardAnnounce(bool available)
{ {
if (!(accessRights & AccessCutText)) return; if (!(accessRights & AccessCutText)) return;
@ -1027,25 +1032,13 @@ void VNCSConnectionST::handleClipboardAnnounce(bool available)
server->handleClipboardAnnounce(this, available); server->handleClipboardAnnounce(this, available);
} }
void VNCSConnectionST::handleClipboardData(const char* data, int len) void VNCSConnectionST::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{ {
if (!(accessRights & AccessCutText)) return; if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::acceptCutText) return; if (!rfb::Server::acceptCutText) return;
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { server->handleClipboardAnnounceBinary(this, num, mimes);
vlog.info("DLP: client %s: refused to receive clipboard, too soon",
sock->getPeerAddress());
return;
}
const int origlen = len;
if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax)
len = rfb::Server::DLP_ClipAcceptMax;
cliplog(data, len, origlen, "received", sock->getPeerAddress());
gettimeofday(&lastClipboardOp, NULL);
server->handleClipboardData(this, data, len);
} }
// supportsLocalCursor() is called whenever the status of // supportsLocalCursor() is called whenever the status of
// cp.supportsLocalCursor has changed. If the client does now support local // cp.supportsLocalCursor has changed. If the client does now support local
// cursor, we make sure that the old server-side rendered cursor is cleaned up // cursor, we make sure that the old server-side rendered cursor is cleaned up
@ -1089,6 +1082,8 @@ bool VNCSConnectionST::handleTimeout(Timer* t)
writeFramebufferUpdate(); writeFramebufferUpdate();
else if (t == &kbdLogTimer) else if (t == &kbdLogTimer)
flushKeylog(sock->getPeerAddress()); flushKeylog(sock->getPeerAddress());
else if (t == &binclipTimer)
writeBinaryClipboard();
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
close(e.str()); close(e.str());
} }
@ -1446,6 +1441,18 @@ void VNCSConnectionST::writeDataUpdate()
requested.clear(); requested.clear();
} }
void VNCSConnectionST::writeBinaryClipboard()
{
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
vlog.info("DLP: client %s: refused to send binary clipboard, too soon",
sock->getPeerAddress());
return;
}
writer()->writeBinaryClipboard(binaryClipboard);
gettimeofday(&lastClipboardOp, NULL);
}
void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
{ {

View File

@ -77,9 +77,12 @@ namespace rfb {
void bellOrClose(); void bellOrClose();
void setDesktopNameOrClose(const char *name); void setDesktopNameOrClose(const char *name);
void setLEDStateOrClose(unsigned int state); void setLEDStateOrClose(unsigned int state);
void requestClipboardOrClose();
void announceClipboardOrClose(bool available); void announceClipboardOrClose(bool available);
void sendClipboardDataOrClose(const char* data); void clearBinaryClipboardData();
void sendBinaryClipboardDataOrClose(const char* mime, const unsigned char *data,
const unsigned len);
void getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len);
// checkIdleTimeout() returns the number of milliseconds left until the // checkIdleTimeout() returns the number of milliseconds left until the
// idle timeout expires. If it has expired, the connection is closed and // idle timeout expires. If it has expired, the connection is closed and
@ -212,9 +215,8 @@ namespace rfb {
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void enableContinuousUpdates(bool enable, virtual void enableContinuousUpdates(bool enable,
int x, int y, int w, int h); int x, int y, int w, int h);
virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardData(const char* data, int len); virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void supportsLocalCursor(); virtual void supportsLocalCursor();
virtual void supportsFence(); virtual void supportsFence();
virtual void supportsContinuousUpdates(); virtual void supportsContinuousUpdates();
@ -260,6 +262,8 @@ namespace rfb {
void writeNoDataUpdate(); void writeNoDataUpdate();
void writeDataUpdate(); void writeDataUpdate();
void writeBinaryClipboard();
void screenLayoutChange(rdr::U16 reason); void screenLayoutChange(rdr::U16 reason);
void setCursor(); void setCursor();
void setCursorPos(); void setCursorPos();
@ -282,6 +286,7 @@ namespace rfb {
Timer congestionTimer; Timer congestionTimer;
Timer losslessTimer; Timer losslessTimer;
Timer kbdLogTimer; Timer kbdLogTimer;
Timer binclipTimer;
VNCServerST* server; VNCServerST* server;
SimpleUpdateTracker updates; SimpleUpdateTracker updates;

View File

@ -52,22 +52,11 @@ namespace rfb {
// getPixelBuffer() returns a pointer to the PixelBuffer object. // getPixelBuffer() returns a pointer to the PixelBuffer object.
virtual PixelBuffer* getPixelBuffer() const = 0; virtual PixelBuffer* getPixelBuffer() const = 0;
// requestClipboard() will result in a request to a client to
// transfer its clipboard data. A call to
// SDesktop::handleClipboardData() will be made once the data is
// available.
virtual void requestClipboard() = 0;
// announceClipboard() informs all clients of changes to the // announceClipboard() informs all clients of changes to the
// clipboard on the server. A client may later request the // clipboard on the server. A client may later request the
// clipboard data via SDesktop::handleClipboardRequest(). // clipboard data via SDesktop::handleClipboardRequest().
virtual void announceClipboard(bool available) = 0; virtual void announceClipboard(bool available) = 0;
// sendClipboardData() transfers the clipboard data to a client
// and should be called whenever a client has requested the
// clipboard via SDesktop::handleClipboardRequest().
virtual void sendClipboardData(const char* data) = 0;
// bell() tells the server that it should make all clients make a bell sound. // bell() tells the server that it should make all clients make a bell sound.
virtual void bell() = 0; virtual void bell() = 0;

View File

@ -518,14 +518,6 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout)
} }
} }
void VNCServerST::requestClipboard()
{
if (clipboardClient == NULL)
return;
clipboardClient->requestClipboard();
}
void VNCServerST::announceClipboard(bool available) void VNCServerST::announceClipboard(bool available)
{ {
std::list<VNCSConnectionST*>::iterator ci, ci_next; std::list<VNCSConnectionST*>::iterator ci, ci_next;
@ -541,20 +533,31 @@ void VNCServerST::announceClipboard(bool available)
} }
} }
void VNCServerST::sendClipboardData(const char* data) void VNCServerST::sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
{ {
std::list<VNCSConnectionST*>::iterator ci, ci_next; std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
if (strchr(data, '\r') != NULL)
throw Exception("Invalid carriage return in clipboard data");
for (ci = clipboardRequestors.begin();
ci != clipboardRequestors.end(); ci = ci_next) {
ci_next = ci; ci_next++; ci_next = ci; ci_next++;
(*ci)->sendClipboardDataOrClose(data); (*ci)->sendBinaryClipboardDataOrClose(mime, data, len);
} }
}
clipboardRequestors.clear(); void VNCServerST::getBinaryClipboardData(const char* mime, const unsigned char **data,
unsigned *len)
{
if (!clipboardClient)
return;
clipboardClient->getBinaryClipboardData(mime, data, len);
}
void VNCServerST::clearBinaryClipboardData()
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->clearBinaryClipboardData();
}
} }
void VNCServerST::bell() void VNCServerST::bell()
@ -1198,13 +1201,6 @@ bool VNCServerST::getComparerState()
return false; return false;
} }
void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
{
clipboardRequestors.push_back(client);
if (clipboardRequestors.size() == 1)
desktop->handleClipboardRequest();
}
void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
bool available) bool available)
{ {
@ -1218,11 +1214,10 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
desktop->handleClipboardAnnounce(available); desktop->handleClipboardAnnounce(available);
} }
void VNCServerST::handleClipboardData(VNCSConnectionST* client, void VNCServerST::handleClipboardAnnounceBinary(VNCSConnectionST* client,
const char* data, int len) const unsigned num,
const char mimes[][32])
{ {
if (client != clipboardClient) clipboardClient = client;
return; desktop->handleClipboardAnnounceBinary(num, mimes);
desktop->handleClipboardData(data, len);
} }

View File

@ -96,9 +96,12 @@ namespace rfb {
virtual void setPixelBuffer(PixelBuffer* pb); virtual void setPixelBuffer(PixelBuffer* pb);
virtual void setScreenLayout(const ScreenSet& layout); virtual void setScreenLayout(const ScreenSet& layout);
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; } virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
virtual void requestClipboard();
virtual void announceClipboard(bool available); virtual void announceClipboard(bool available);
virtual void sendClipboardData(const char* data); virtual void clearBinaryClipboardData();
virtual void sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
virtual void getBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
virtual void add_changed(const Region &region); virtual void add_changed(const Region &region);
virtual void add_copied(const Region &dest, const Point &delta); virtual void add_copied(const Region &dest, const Point &delta);
virtual void setCursor(int width, int height, const Point& hotspot, virtual void setCursor(int width, int height, const Point& hotspot,
@ -191,9 +194,9 @@ namespace rfb {
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; } void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
void handleClipboardRequest(VNCSConnectionST* client);
void handleClipboardAnnounce(VNCSConnectionST* client, bool available); void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
void handleClipboardData(VNCSConnectionST* client, const char* data, int len); void handleClipboardAnnounceBinary(VNCSConnectionST* client, const unsigned num,
const char mimes[][32]);
protected: protected:

View File

@ -31,6 +31,7 @@ namespace rfb {
// kasm // kasm
const int msgTypeStats = 178; const int msgTypeStats = 178;
const int msgTypeRequestFrameStats = 179; const int msgTypeRequestFrameStats = 179;
const int msgTypeBinaryClipboard = 180;
const int msgTypeServerFence = 248; const int msgTypeServerFence = 248;
@ -49,6 +50,7 @@ namespace rfb {
// kasm // kasm
const int msgTypeRequestStats = 178; const int msgTypeRequestStats = 178;
const int msgTypeFrameStats = 179; const int msgTypeFrameStats = 179;
//const int msgTypeBinaryClipboard = 180;
const int msgTypeClientFence = 248; const int msgTypeClientFence = 248;

View File

@ -69,7 +69,7 @@ void SSE2_halve(const uint8_t *oldpx,
uint8_t * const dst = newpx + newstride * (y / 2) * 4; uint8_t * const dst = newpx + newstride * (y / 2) * 4;
for (x = 0; x < srcw; x += 4) { for (x = 0; x < srcw - 3; x += 4) {
__m128i lo, hi, a, b, c, d; __m128i lo, hi, a, b, c, d;
lo = _mm_loadu_si128((__m128i *) &row0[x * 4]); lo = _mm_loadu_si128((__m128i *) &row0[x * 4]);
hi = _mm_loadu_si128((__m128i *) &row1[x * 4]); hi = _mm_loadu_si128((__m128i *) &row1[x * 4]);
@ -141,7 +141,7 @@ void SSE2_scale(const uint8_t *oldpx,
const __m128i vertmul = _mm_set1_epi16(top); const __m128i vertmul = _mm_set1_epi16(top);
const __m128i vertmul2 = _mm_set1_epi16(bot); const __m128i vertmul2 = _mm_set1_epi16(bot);
for (x = 0; x < tgtw; x += 2) { for (x = 0; x < tgtw - 1; x += 2) {
const float nx[2] = { const float nx[2] = {
x * invdiff, x * invdiff,
(x + 1) * invdiff, (x + 1) * invdiff,

@ -1 +1 @@
Subproject commit a4cf38902911e9ff07e9d3eb7909c5ba932a5089 Subproject commit 0bd38139494ef1bbaf580c2f4892c7be3a291b19

View File

@ -176,15 +176,6 @@ XserverDesktop::queryConnection(network::Socket* sock,
return rfb::VNCServerST::PENDING; return rfb::VNCServerST::PENDING;
} }
void XserverDesktop::requestClipboard()
{
try {
server->requestClipboard();
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::requestClipboard: %s",e.str());
}
}
void XserverDesktop::announceClipboard(bool available) void XserverDesktop::announceClipboard(bool available)
{ {
try { try {
@ -194,12 +185,34 @@ void XserverDesktop::announceClipboard(bool available)
} }
} }
void XserverDesktop::sendClipboardData(const char* data) void XserverDesktop::clearBinaryClipboardData()
{ {
try { try {
server->sendClipboardData(data); server->clearBinaryClipboardData();
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
vlog.error("XserverDesktop::sendClipboardData: %s",e.str()); vlog.error("XserverDesktop::clearBinaryClipboardData: %s",e.str());
}
}
void XserverDesktop::sendBinaryClipboardData(const char* mime,
const unsigned char *data,
const unsigned len)
{
try {
server->sendBinaryClipboardData(mime, data, len);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::sendBinaryClipboardData: %s",e.str());
}
}
void XserverDesktop::getBinaryClipboardData(const char* mime,
const unsigned char **data,
unsigned *len)
{
try {
server->getBinaryClipboardData(mime, data, len);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::getBinaryClipboardData: %s",e.str());
} }
} }
@ -472,19 +485,14 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height,
return ret; return ret;
} }
void XserverDesktop::handleClipboardRequest()
{
vncHandleClipboardRequest();
}
void XserverDesktop::handleClipboardAnnounce(bool available) void XserverDesktop::handleClipboardAnnounce(bool available)
{ {
vncHandleClipboardAnnounce(available); vncHandleClipboardAnnounce(available);
} }
void XserverDesktop::handleClipboardData(const char* data_, int len) void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{ {
vncHandleClipboardData(data_, len); vncHandleClipboardAnnounceBinary(num, mimes);
} }
void XserverDesktop::grabRegion(const rfb::Region& region) void XserverDesktop::grabRegion(const rfb::Region& region)

View File

@ -62,7 +62,11 @@ public:
void refreshScreenLayout(); void refreshScreenLayout();
void requestClipboard(); void requestClipboard();
void announceClipboard(bool available); void announceClipboard(bool available);
void sendClipboardData(const char* data); void clearBinaryClipboardData();
void sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
void getBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
void bell(); void bell();
void setLEDState(unsigned int state); void setLEDState(unsigned int state);
void setDesktopName(const char* name); void setDesktopName(const char* name);
@ -94,9 +98,8 @@ public:
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual unsigned int setScreenLayout(int fb_width, int fb_height, virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout); const rfb::ScreenSet& layout);
virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardData(const char* data, int len); virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
// rfb::PixelBuffer callbacks // rfb::PixelBuffer callbacks
virtual void grabRegion(const rfb::Region& r); virtual void grabRegion(const rfb::Region& r);

View File

@ -295,17 +295,22 @@ Allow click releases inside the blacked-out region.
. .
.TP .TP
.B \-DLP_ClipSendMax \fIbytes\fP .B \-DLP_ClipSendMax \fIbytes\fP
Limit clipboard bytes to send to clients in one transaction. Default 10,000. Limit clipboard bytes to send to clients in one transaction. Default 0.
0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely. 0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.
. .
.TP .TP
.B \-DLP_ClipAcceptMax \fIbytes\fP .B \-DLP_ClipAcceptMax \fIbytes\fP
Limit clipboard bytes to receive from clients in one transaction. Default 10,000. Limit clipboard bytes to receive from clients in one transaction. Default 0.
0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely. 0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely.
. .
.TP .TP
.B \-DLP_ClipDelay \fIms\fP .B \-DLP_ClipDelay \fIms\fP
This many milliseconds must pass between clipboard actions. Default 1000. This many milliseconds must pass between clipboard actions. Default 0, 0 disables the limit.
.
.TP
.B \-DLP_ClipTypes \fIa,b\fP
Allowed binary clipboard mimetypes, separated by commas. Default
chromium/x-web-custom-data,text/html,image/png
. .
.TP .TP
.B \-DLP_KeyRateLimit \fIkeys-per-second\fP .B \-DLP_KeyRateLimit \fIkeys-per-second\fP

View File

@ -18,10 +18,12 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <errno.h> #include <errno.h>
#include <set> #include <set>
#include <string> #include <string>
#include <vector>
#include <rfb/Configuration.h> #include <rfb/Configuration.h>
#include <rfb/Logger_stdio.h> #include <rfb/Logger_stdio.h>
@ -64,6 +66,8 @@ int vncFbstride[MAXSCREENS];
int vncInetdSock = -1; int vncInetdSock = -1;
static std::vector<dlp_mimetype_t> dlp_mimetypes;
struct CaseInsensitiveCompare { struct CaseInsensitiveCompare {
bool operator() (const std::string &a, const std::string &b) const { bool operator() (const std::string &a, const std::string &b) const {
return strcasecmp(a.c_str(), b.c_str()) < 0; return strcasecmp(a.c_str(), b.c_str()) < 0;
@ -147,6 +151,38 @@ static void parseOverrideList(const char *text, ParamSet &out)
} }
} }
static void parseClipTypes()
{
char *str = strdup(Server::DLP_Clip_Types);
char * const origstr = str;
while (1) {
char *cur = strsep(&str, ",");
if (!cur)
break;
if (!cur[0])
continue;
// Hardcoded filters
if (!strcmp(cur, "TEXT") ||
!strcmp(cur, "STRING") ||
strstr(cur, "text/plain") ||
!strcmp(cur, "UTF8_STRING"))
continue;
struct dlp_mimetype_t m;
strncpy(m.mime, cur, sizeof(m.mime));
m.mime[sizeof(m.mime) - 1] = '\0';
dlp_mimetypes.push_back(m);
vlog.debug("Adding DLP binary mime type %s", m.mime);
}
vlog.debug("Total %u binary mime types", dlp_mimetypes.size());
free(origstr);
}
void vncExtensionInit(void) void vncExtensionInit(void)
{ {
if (vncExtGeneration == vncGetServerGeneration()) { if (vncExtGeneration == vncGetServerGeneration()) {
@ -163,6 +199,8 @@ void vncExtensionInit(void)
vncAddExtension(); vncAddExtension();
if (!initialised)
parseClipTypes();
vncSelectionInit(); vncSelectionInit();
vlog.info("VNC extension running!"); vlog.info("VNC extension running!");
@ -315,22 +353,30 @@ void vncUpdateDesktopName(void)
desktop[scr]->setDesktopName(desktopName); desktop[scr]->setDesktopName(desktopName);
} }
void vncRequestClipboard(void)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->requestClipboard();
}
void vncAnnounceClipboard(int available) void vncAnnounceClipboard(int available)
{ {
for (int scr = 0; scr < vncGetScreenCount(); scr++) for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->announceClipboard(available); desktop[scr]->announceClipboard(available);
} }
void vncSendClipboardData(const char* data) void vncSendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
{ {
for (int scr = 0; scr < vncGetScreenCount(); scr++) for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->sendClipboardData(data); desktop[scr]->sendBinaryClipboardData(mime, data, len);
}
void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->getBinaryClipboardData(mime, ptr, len);
}
void vncClearBinaryClipboardData()
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
desktop[scr]->clearBinaryClipboardData();
} }
int vncConnectClient(const char *addr) int vncConnectClient(const char *addr)
@ -486,3 +532,13 @@ int vncOverrideParam(const char *nameAndValue)
return rfb::Configuration::setParam(nameAndValue); return rfb::Configuration::setParam(nameAndValue);
} }
unsigned dlp_num_mimetypes()
{
return dlp_mimetypes.size();
}
const char *dlp_get_mimetype(const unsigned i)
{
return dlp_mimetypes[i].mime;
}

View File

@ -45,6 +45,13 @@ int vncNotifyQueryConnect(void);
extern void* vncFbptr[]; extern void* vncFbptr[];
extern int vncFbstride[]; extern int vncFbstride[];
struct dlp_mimetype_t {
char mime[32];
};
unsigned dlp_num_mimetypes();
const char *dlp_get_mimetype(const unsigned i);
extern int vncInetdSock; extern int vncInetdSock;
void vncExtensionInit(void); void vncExtensionInit(void);
@ -60,9 +67,12 @@ int vncGetSendPrimary(void);
void vncUpdateDesktopName(void); void vncUpdateDesktopName(void);
void vncRequestClipboard(void);
void vncAnnounceClipboard(int available); void vncAnnounceClipboard(int available);
void vncSendClipboardData(const char* data); void vncClearBinaryClipboardData();
void vncSendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len);
void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr,
unsigned *len);
int vncConnectClient(const char *addr); int vncConnectClient(const char *addr);

View File

@ -43,6 +43,14 @@
static Atom xaPRIMARY, xaCLIPBOARD; static Atom xaPRIMARY, xaCLIPBOARD;
static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING; static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING;
static Atom xaINCR;
static Atom *xaBinclips;
static unsigned xaHtmlIndex, xaPngIndex;
static Bool htmlPngPresent;
static unsigned *mimeIndexesFromClient;
static unsigned numMimesFromClient;
static Bool textFromClient;
static WindowPtr pWindow; static WindowPtr pWindow;
static Window wid; static Window wid;
@ -66,8 +74,7 @@ static int vncCreateSelectionWindow(void);
static int vncOwnSelection(Atom selection); static int vncOwnSelection(Atom selection);
static int vncConvertSelection(ClientPtr client, Atom selection, static int vncConvertSelection(ClientPtr client, Atom selection,
Atom target, Atom property, Atom target, Atom property,
Window requestor, CARD32 time, Window requestor, CARD32 time);
const char* data, int len);
static int vncProcConvertSelection(ClientPtr client); static int vncProcConvertSelection(ClientPtr client);
static void vncSelectionRequest(Atom selection, Atom target); static void vncSelectionRequest(Atom selection, Atom target);
static int vncProcSendEvent(ClientPtr client); static int vncProcSendEvent(ClientPtr client);
@ -90,6 +97,32 @@ void vncSelectionInit(void)
xaTEXT = MakeAtom("TEXT", 4, TRUE); xaTEXT = MakeAtom("TEXT", 4, TRUE);
xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE); xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE);
xaINCR = MakeAtom("INCR", 4, TRUE);
unsigned i;
mimeIndexesFromClient = calloc(dlp_num_mimetypes(), sizeof(unsigned));
numMimesFromClient = 0;
textFromClient = FALSE;
xaBinclips = calloc(dlp_num_mimetypes(), sizeof(Atom));
htmlPngPresent = FALSE;
Bool htmlfound = FALSE, pngfound = FALSE;
for (i = 0; i < dlp_num_mimetypes(); i++) {
const char *cur = dlp_get_mimetype(i);
xaBinclips[i] = MakeAtom(cur, strlen(cur), TRUE);
if (!strcmp(cur, "text/html")) {
xaHtmlIndex = i;
htmlfound = TRUE;
}
if (!strcmp(cur, "image/png")) {
xaPngIndex = i;
pngfound = TRUE;
}
}
if (htmlfound && pngfound)
htmlPngPresent = TRUE;
/* There are no hooks for when these are internal windows, so /* There are no hooks for when these are internal windows, so
* override the relevant handlers. */ * override the relevant handlers. */
origProcConvertSelection = ProcVector[X_ConvertSelection]; origProcConvertSelection = ProcVector[X_ConvertSelection];
@ -103,7 +136,7 @@ void vncSelectionInit(void)
FatalError("Add VNC ClientStateCallback failed\n"); FatalError("Add VNC ClientStateCallback failed\n");
} }
void vncHandleClipboardRequest(void) static void vncHandleClipboardRequest(void)
{ {
if (activeSelection == None) { if (activeSelection == None) {
LOG_DEBUG("Got request for local clipboard although no clipboard is active"); LOG_DEBUG("Got request for local clipboard although no clipboard is active");
@ -161,24 +194,55 @@ void vncHandleClipboardAnnounce(int available)
} }
} }
void vncHandleClipboardData(const char* data, int len) void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{ {
struct VncDataTarget* next; if (num) {
LOG_DEBUG("Got remote clipboard data, sending to X11 clients");
while (vncDataTargetHead != NULL) {
int rc; int rc;
LOG_DEBUG("Remote binary clipboard announced, grabbing local ownership");
if (vncGetSetPrimary()) {
rc = vncOwnSelection(xaPRIMARY);
if (rc != Success)
LOG_ERROR("Could not set PRIMARY selection");
}
rc = vncOwnSelection(xaCLIPBOARD);
if (rc != Success)
LOG_ERROR("Could not set CLIPBOARD selection");
unsigned i, valid = 0;
for (i = 0; i < num; i++) {
unsigned j;
for (j = 0; j < dlp_num_mimetypes(); j++) {
if (!strcmp(dlp_get_mimetype(j), mimes[i])) {
mimeIndexesFromClient[valid] = j;
valid++;
break;
}
}
if (!strcmp(mimes[i], "text/plain"))
textFromClient = TRUE;
}
numMimesFromClient = valid;
LOG_DEBUG("Client sent %u mimes, %u were valid", num, valid);
} else {
struct VncDataTarget* next;
numMimesFromClient = 0;
textFromClient = FALSE;
if (pWindow == NULL)
return;
LOG_DEBUG("Remote binary clipboard lost, removing local ownership");
DeleteWindowFromAnySelections(pWindow);
/* Abort any pending transfer */
while (vncDataTargetHead != NULL) {
xEvent event; xEvent event;
rc = vncConvertSelection(vncDataTargetHead->client,
vncDataTargetHead->selection,
vncDataTargetHead->target,
vncDataTargetHead->property,
vncDataTargetHead->requestor,
vncDataTargetHead->time,
data, len);
if (rc != Success) {
event.u.u.type = SelectionNotify; event.u.u.type = SelectionNotify;
event.u.selectionNotify.time = vncDataTargetHead->time; event.u.selectionNotify.time = vncDataTargetHead->time;
event.u.selectionNotify.requestor = vncDataTargetHead->requestor; event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
@ -186,12 +250,12 @@ void vncHandleClipboardData(const char* data, int len)
event.u.selectionNotify.target = vncDataTargetHead->target; event.u.selectionNotify.target = vncDataTargetHead->target;
event.u.selectionNotify.property = None; event.u.selectionNotify.property = None;
WriteEventsToClient(vncDataTargetHead->client, 1, &event); WriteEventsToClient(vncDataTargetHead->client, 1, &event);
}
next = vncDataTargetHead->next; next = vncDataTargetHead->next;
free(vncDataTargetHead); free(vncDataTargetHead);
vncDataTargetHead = next; vncDataTargetHead = next;
} }
}
} }
static int vncCreateSelectionWindow(void) static int vncCreateSelectionWindow(void)
@ -277,10 +341,22 @@ static int vncOwnSelection(Atom selection)
return Success; return Success;
} }
static Bool clientHasBinaryAtom(const Atom target, unsigned *which)
{
unsigned i;
for (i = 0; i < numMimesFromClient; i++) {
if (xaBinclips[mimeIndexesFromClient[i]] == target) {
*which = mimeIndexesFromClient[i];
return TRUE;
}
}
return FALSE;
}
static int vncConvertSelection(ClientPtr client, Atom selection, static int vncConvertSelection(ClientPtr client, Atom selection,
Atom target, Atom property, Atom target, Atom property,
Window requestor, CARD32 time, Window requestor, CARD32 time)
const char* data, int len)
{ {
Selection *pSel; Selection *pSel;
WindowPtr pWin; WindowPtr pWin;
@ -290,13 +366,8 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
xEvent event; xEvent event;
if (data == NULL) {
LOG_DEBUG("Selection request for %s (type %s)", LOG_DEBUG("Selection request for %s (type %s)",
NameForAtom(selection), NameForAtom(target)); NameForAtom(selection), NameForAtom(target));
} else {
LOG_DEBUG("Sending data for selection request for %s (type %s)",
NameForAtom(selection), NameForAtom(target));
}
rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess); rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess);
if (rc != Success) if (rc != Success)
@ -315,14 +386,23 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
realProperty = target; realProperty = target;
/* FIXME: MULTIPLE target */ /* FIXME: MULTIPLE target */
unsigned binatomidx;
if (target == xaTARGETS) { if (target == xaTARGETS) {
Atom targets[] = { xaTARGETS, xaTIMESTAMP, Atom targets[5 + numMimesFromClient];
xaSTRING, xaTEXT, xaUTF8_STRING }; targets[0] = xaTARGETS;
targets[1] = xaTIMESTAMP;
targets[2] = xaSTRING;
targets[3] = xaTEXT;
targets[4] = xaUTF8_STRING;
unsigned i;
for (i = 0; i < numMimesFromClient; i++)
targets[5 + i] = xaBinclips[mimeIndexesFromClient[i]];
rc = dixChangeWindowProperty(serverClient, pWin, realProperty, rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
XA_ATOM, 32, PropModeReplace, XA_ATOM, 32, PropModeReplace,
sizeof(targets)/sizeof(targets[0]), 5 + numMimesFromClient,
targets, TRUE); targets, TRUE);
if (rc != Success) if (rc != Success)
return rc; return rc;
@ -333,37 +413,22 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
TRUE); TRUE);
if (rc != Success) if (rc != Success)
return rc; return rc;
} else { } else if (clientHasBinaryAtom(target, &binatomidx)) {
if (data == NULL) { const unsigned char *data;
struct VncDataTarget* vdt; unsigned len;
vncGetBinaryClipboardData(dlp_get_mimetype(binatomidx), &data, &len);
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaBinclips[binatomidx], 8, PropModeReplace,
len, (unsigned char *) data, TRUE);
if (rc != Success)
return rc;
} else if (textFromClient) {
const unsigned char *data;
unsigned len;
vncGetBinaryClipboardData("text/plain", &data, &len);
if ((target != xaSTRING) && (target != xaTEXT) &&
(target != xaUTF8_STRING))
return BadMatch;
vdt = calloc(1, sizeof(struct VncDataTarget));
if (vdt == NULL)
return BadAlloc;
vdt->client = client;
vdt->selection = selection;
vdt->target = target;
vdt->property = property;
vdt->requestor = requestor;
vdt->time = time;
vdt->next = vncDataTargetHead;
vncDataTargetHead = vdt;
LOG_DEBUG("Requesting clipboard data from client");
vncRequestClipboard();
return Success;
} else {
if ((target == xaSTRING) || (target == xaTEXT)) { if ((target == xaSTRING) || (target == xaTEXT)) {
char* latin1; char* latin1;
latin1 = vncUTF8ToLatin1(data, (size_t)-1); latin1 = vncUTF8ToLatin1(data, (size_t)-1);
if (latin1 == NULL) if (latin1 == NULL)
return BadAlloc; return BadAlloc;
@ -379,13 +444,16 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
} else if (target == xaUTF8_STRING) { } else if (target == xaUTF8_STRING) {
rc = dixChangeWindowProperty(serverClient, pWin, realProperty, rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaUTF8_STRING, 8, PropModeReplace, xaUTF8_STRING, 8, PropModeReplace,
len, data, TRUE); len, (char *) data, TRUE);
if (rc != Success) if (rc != Success)
return rc; return rc;
} else { } else {
return BadMatch; return BadMatch;
} }
} } else {
LOG_ERROR("Text clipboard paste requested, but client sent no text");
return BadMatch;
} }
event.u.u.type = SelectionNotify; event.u.u.type = SelectionNotify;
@ -424,7 +492,7 @@ static int vncProcConvertSelection(ClientPtr client)
pSel->window == wid) { pSel->window == wid) {
rc = vncConvertSelection(client, stuff->selection, rc = vncConvertSelection(client, stuff->selection,
stuff->target, stuff->property, stuff->target, stuff->property,
stuff->requestor, stuff->time, NULL, 0); stuff->requestor, stuff->time);
if (rc != Success) { if (rc != Success) {
xEvent event; xEvent event;
@ -483,6 +551,21 @@ static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
return FALSE; return FALSE;
} }
static Bool vncHasBinaryClipboardAtom(const Atom list[], size_t size)
{
size_t i, b;
const unsigned num = dlp_num_mimetypes();
for (i = 0;i < size;i++) {
for (b = 0; b < num; b++) {
if (list[i] == xaBinclips[b])
return TRUE;
}
}
return FALSE;
}
static void vncHandleSelection(Atom selection, Atom target, static void vncHandleSelection(Atom selection, Atom target,
Atom property, Atom requestor, Atom property, Atom requestor,
TimeStamp time) TimeStamp time)
@ -502,6 +585,9 @@ static void vncHandleSelection(Atom selection, Atom target,
if (target != property) if (target != property)
return; return;
if (prop->type == xaINCR)
LOG_INFO("Incremental clipboard transfer denied, too large");
if (target == xaTARGETS) { if (target == xaTARGETS) {
if (prop->format != 32) if (prop->format != 32)
return; return;
@ -510,16 +596,36 @@ static void vncHandleSelection(Atom selection, Atom target,
if (probing) { if (probing) {
if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) || if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size) ||
vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) { vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size) ||
vncHasBinaryClipboardAtom((const Atom*)prop->data, prop->size)) {
LOG_DEBUG("Compatible format found, notifying clients"); LOG_DEBUG("Compatible format found, notifying clients");
vncClearBinaryClipboardData();
activeSelection = selection; activeSelection = selection;
vncAnnounceClipboard(TRUE); vncAnnounceClipboard(TRUE);
vncHandleClipboardRequest();
} }
} else { } else {
if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaUTF8_STRING); vncSelectionRequest(selection, xaUTF8_STRING);
else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaSTRING); vncSelectionRequest(selection, xaSTRING);
unsigned i;
Bool skiphtml = FALSE;
if (htmlPngPresent &&
vncHasAtom(xaBinclips[xaHtmlIndex], (const Atom*)prop->data, prop->size) &&
vncHasAtom(xaBinclips[xaPngIndex], (const Atom*)prop->data, prop->size))
skiphtml = TRUE;
for (i = 0; i < dlp_num_mimetypes(); i++) {
if (skiphtml && i == xaHtmlIndex)
continue;
if (vncHasAtom(xaBinclips[i], (const Atom*)prop->data, prop->size)) {
vncSelectionRequest(selection, xaBinclips[i]);
//break;
}
}
} }
} else if (target == xaSTRING) { } else if (target == xaSTRING) {
char* filtered; char* filtered;
@ -539,10 +645,10 @@ static void vncHandleSelection(Atom selection, Atom target,
if (utf8 == NULL) if (utf8 == NULL)
return; return;
LOG_DEBUG("Sending clipboard to clients (%d bytes)", LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)",
(int)strlen(utf8)); (int)strlen(utf8));
vncSendClipboardData(utf8); vncSendBinaryClipboardData("text/plain", utf8, strlen(utf8));
vncStrFree(utf8); vncStrFree(utf8);
} else if (target == xaUTF8_STRING) { } else if (target == xaUTF8_STRING) {
@ -557,12 +663,31 @@ static void vncHandleSelection(Atom selection, Atom target,
if (filtered == NULL) if (filtered == NULL)
return; return;
LOG_DEBUG("Sending clipboard to clients (%d bytes)", LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)",
(int)strlen(filtered)); (int)strlen(filtered));
vncSendClipboardData(filtered); vncSendBinaryClipboardData("text/plain", filtered, strlen(filtered));
vncStrFree(filtered); vncStrFree(filtered);
} else {
unsigned i;
if (prop->format != 8)
return;
for (i = 0; i < dlp_num_mimetypes(); i++) {
if (target == xaBinclips[i]) {
if (prop->type != xaBinclips[i])
return;
LOG_DEBUG("Sending binary clipboard to clients (%d bytes)",
prop->size);
vncSendBinaryClipboardData(dlp_get_mimetype(i), prop->data, prop->size);
break;
}
}
} }
} }

View File

@ -24,9 +24,8 @@ extern "C" {
void vncSelectionInit(void); void vncSelectionInit(void);
void vncHandleClipboardRequest(void);
void vncHandleClipboardAnnounce(int available); void vncHandleClipboardAnnounce(int available);
void vncHandleClipboardData(const char* data, int len); void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
#ifdef __cplusplus #ifdef __cplusplus
} }