Initial binary clipboard support

pull/69/head
Lauri Kasanen 4 years ago
parent 0cb2c0ba9f
commit 53c8de789f

@ -353,6 +353,25 @@ void SConnection::handleClipboardProvide(rdr::U32 flags,
handleClipboardData(clientClipboard, strlen(clientClipboard)); handleClipboardData(clientClipboard, strlen(clientClipboard));
} }
void SConnection::clearBinaryClipboard()
{
binaryClipboard.clear();
}
void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len)
{
binaryClipboard_t bin;
strncpy(bin.mime, mime, sizeof(bin.mime));
bin.mime[sizeof(bin.mime) - 1] = '\0';
bin.data.resize(len);
memcpy(&bin.data[0], data, len);
binaryClipboard.push_back(bin);
}
void SConnection::supportsQEMUKeyEvent() void SConnection::supportsQEMUKeyEvent()
{ {
writer()->writeQEMUKeyEvent(); writer()->writeQEMUKeyEvent();

@ -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 {
@ -81,6 +82,9 @@ namespace rfb {
virtual void handleClipboardProvide(rdr::U32 flags, virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths, const size_t* lengths,
const rdr::U8* const* data); const rdr::U8* const* data);
virtual void clearBinaryClipboard();
virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len);
virtual void supportsQEMUKeyEvent(); virtual void supportsQEMUKeyEvent();
@ -221,12 +225,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);

@ -89,6 +89,9 @@ namespace rfb {
// access the actual data. // access the actual data.
virtual void handleClipboardAnnounce(bool __unused_attr available) {} virtual void handleClipboardAnnounce(bool __unused_attr available) {}
virtual void handleClipboardAnnounceBinary(const unsigned __unused_attr num,
const char __unused_attr mimes[][32]) {}
// handleClipboardData() is called when a client has sent over // handleClipboardData() is called when a client has sent over
// the clipboard data as a result of a previous call to // the clipboard data as a result of a previous call to
// VNCServer::requestClipboard(). Note that this function might // VNCServer::requestClipboard(). Note that this function might

@ -69,6 +69,10 @@ void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths)
cp.setClipboardCaps(flags, lengths); cp.setClipboardCaps(flags, lengths);
} }
void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32])
{
}
void SMsgHandler::handleClipboardRequest(rdr::U32 flags) void SMsgHandler::handleClipboardRequest(rdr::U32 flags)
{ {
} }
@ -87,6 +91,15 @@ void SMsgHandler::handleClipboardProvide(rdr::U32 flags,
{ {
} }
void SMsgHandler::clearBinaryClipboard()
{
}
void SMsgHandler::addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len)
{
}
void SMsgHandler::supportsLocalCursor() void SMsgHandler::supportsLocalCursor()
{ {
} }

@ -54,6 +54,7 @@ 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 handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void handleClipboardCaps(rdr::U32 flags, virtual void handleClipboardCaps(rdr::U32 flags,
const rdr::U32* lengths); const rdr::U32* lengths);
virtual void handleClipboardRequest(rdr::U32 flags); virtual void handleClipboardRequest(rdr::U32 flags);
@ -62,6 +63,9 @@ namespace rfb {
virtual void handleClipboardProvide(rdr::U32 flags, virtual void handleClipboardProvide(rdr::U32 flags,
const size_t* lengths, const size_t* lengths,
const rdr::U8* const* data); const rdr::U8* const* data);
virtual void clearBinaryClipboard();
virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data,
const rdr::U32 len);
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;

@ -83,6 +83,9 @@ void SMsgReader::readMsg()
case msgTypeFrameStats: case msgTypeFrameStats:
readFrameStats(); readFrameStats();
break; break;
case msgTypeBinaryClipboard:
readBinaryClipboard();
break;
case msgTypeKeyEvent: case msgTypeKeyEvent:
readKeyEvent(); readKeyEvent();
break; break;
@ -249,6 +252,44 @@ void SMsgReader::readClientCutText()
handler->clientCutText(ca.buf, len); handler->clientCutText(ca.buf, len);
} }
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);
}
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);
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 flags;

@ -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();

@ -200,6 +200,24 @@ void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
endMsg(); endMsg();
} }
void SMsgWriter::writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b)
{
startMsg(msgTypeBinaryClipboard);
os->writeU8(b.size());
rdr::U8 i;
for (i = 0; i < b.size(); i++) {
const rdr::U8 mimelen = strlen(b[i].mime);
os->writeU8(mimelen);
os->writeBytes(b[i].mime, mimelen);
os->writeU32(b[i].data.size());
os->writeBytes(&b[i].data[0], b[i].data.size());
}
endMsg();
}
void SMsgWriter::writeStats(const char* str, int len) void SMsgWriter::writeStats(const char* str, int len)
{ {
startMsg(msgTypeStats); startMsg(msgTypeStats);

@ -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; }
@ -62,6 +64,7 @@ namespace rfb {
void writeClipboardNotify(rdr::U32 flags); void writeClipboardNotify(rdr::U32 flags);
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths, void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
const rdr::U8* const* data); const rdr::U8* const* data);
void writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b);
void writeStats(const char* str, int len); void writeStats(const char* str, int len);

@ -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_Clip_Types",
"Allowed binary clipboard mimetypes",
"chromium/x-web-custom-data,text/html,image/png");
rfb::BoolParameter rfb::Server::DLP_RegionAllowClick rfb::BoolParameter rfb::Server::DLP_RegionAllowClick
("DLP_RegionAllowClick", ("DLP_RegionAllowClick",

@ -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;

@ -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),
@ -460,6 +461,58 @@ void VNCSConnectionST::sendClipboardDataOrClose(const char* data)
} }
} }
void VNCSConnectionST::clearBinaryClipboardData()
{
clearBinaryClipboard();
}
void VNCSConnectionST::sendBinaryClipboardDataOrClose(const char* mime,
const unsigned char *data,
const unsigned len)
{
try {
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::sendCutText) return;
if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) {
vlog.info("DLP: client %s: refused to send binary clipboard, too soon",
sock->getPeerAddress());
return;
}
if (rfb::Server::DLP_ClipSendMax && len > (unsigned) rfb::Server::DLP_ClipSendMax) {
vlog.info("DLP: client %s: refused to send binary clipboard, too large",
sock->getPeerAddress());
return;
}
cliplog((const char *) data, len, len, "sent", sock->getPeerAddress());
if (state() != RFBSTATE_NORMAL) return;
addBinaryClipboard(mime, data, len);
binclipTimer.start(100);
gettimeofday(&lastClipboardOp, NULL);
} catch(rdr::Exception& e) {
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 {
@ -1027,6 +1080,13 @@ void VNCSConnectionST::handleClipboardAnnounce(bool available)
server->handleClipboardAnnounce(this, available); server->handleClipboardAnnounce(this, available);
} }
void VNCSConnectionST::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{
if (!(accessRights & AccessCutText)) return;
if (!rfb::Server::acceptCutText) return;
server->handleClipboardAnnounceBinary(this, num, mimes);
}
void VNCSConnectionST::handleClipboardData(const char* data, int len) void VNCSConnectionST::handleClipboardData(const char* data, int len)
{ {
if (!(accessRights & AccessCutText)) return; if (!(accessRights & AccessCutText)) return;
@ -1089,6 +1149,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 +1508,10 @@ void VNCSConnectionST::writeDataUpdate()
requested.clear(); requested.clear();
} }
void VNCSConnectionST::writeBinaryClipboard()
{
writer()->writeBinaryClipboard(binaryClipboard);
}
void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
{ {

@ -80,6 +80,11 @@ namespace rfb {
void requestClipboardOrClose(); void requestClipboardOrClose();
void announceClipboardOrClose(bool available); void announceClipboardOrClose(bool available);
void sendClipboardDataOrClose(const char* data); 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
@ -214,6 +219,7 @@ namespace rfb {
int x, int y, int w, int h); int x, int y, int w, int h);
virtual void handleClipboardRequest(); virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void handleClipboardData(const char* data, int len); virtual void handleClipboardData(const char* data, int len);
virtual void supportsLocalCursor(); virtual void supportsLocalCursor();
virtual void supportsFence(); virtual void supportsFence();
@ -260,6 +266,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 +290,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;

@ -557,6 +557,33 @@ void VNCServerST::sendClipboardData(const char* data)
clipboardRequestors.clear(); clipboardRequestors.clear();
} }
void VNCServerST::sendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
{
std::list<VNCSConnectionST*>::iterator ci, ci_next;
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
ci_next = ci; ci_next++;
(*ci)->sendBinaryClipboardDataOrClose(mime, data, len);
}
}
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()
{ {
std::list<VNCSConnectionST*>::iterator ci, ci_next; std::list<VNCSConnectionST*>::iterator ci, ci_next;
@ -1218,6 +1245,14 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
desktop->handleClipboardAnnounce(available); desktop->handleClipboardAnnounce(available);
} }
void VNCServerST::handleClipboardAnnounceBinary(VNCSConnectionST* client,
const unsigned num,
const char mimes[][32])
{
clipboardClient = client;
desktop->handleClipboardAnnounceBinary(num, mimes);
}
void VNCServerST::handleClipboardData(VNCSConnectionST* client, void VNCServerST::handleClipboardData(VNCSConnectionST* client,
const char* data, int len) const char* data, int len)
{ {

@ -99,6 +99,11 @@ namespace rfb {
virtual void requestClipboard(); virtual void requestClipboard();
virtual void announceClipboard(bool available); virtual void announceClipboard(bool available);
virtual void sendClipboardData(const char* data); 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,
@ -193,6 +198,8 @@ namespace rfb {
void handleClipboardRequest(VNCSConnectionST* client); void handleClipboardRequest(VNCSConnectionST* client);
void handleClipboardAnnounce(VNCSConnectionST* client, bool available); void handleClipboardAnnounce(VNCSConnectionST* client, bool available);
void handleClipboardAnnounceBinary(VNCSConnectionST* client, const unsigned num,
const char mimes[][32]);
void handleClipboardData(VNCSConnectionST* client, const char* data, int len); void handleClipboardData(VNCSConnectionST* client, const char* data, int len);
protected: protected:

@ -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;

@ -203,6 +203,37 @@ void XserverDesktop::sendClipboardData(const char* data)
} }
} }
void XserverDesktop::clearBinaryClipboardData()
{
try {
server->clearBinaryClipboardData();
} catch (rdr::Exception& e) {
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());
}
}
void XserverDesktop::bell() void XserverDesktop::bell()
{ {
server->bell(); server->bell();
@ -479,6 +510,11 @@ void XserverDesktop::handleClipboardAnnounce(bool available)
vncHandleClipboardAnnounce(available); vncHandleClipboardAnnounce(available);
} }
void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{
vncHandleClipboardAnnounceBinary(num, mimes);
}
void XserverDesktop::handleClipboardData(const char* data_, int len) void XserverDesktop::handleClipboardData(const char* data_, int len)
{ {
vncHandleClipboardData(data_, len); vncHandleClipboardData(data_, len);

@ -63,6 +63,11 @@ public:
void requestClipboard(); void requestClipboard();
void announceClipboard(bool available); void announceClipboard(bool available);
void sendClipboardData(const char* data); 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);
@ -96,6 +101,7 @@ public:
const rfb::ScreenSet& layout); const rfb::ScreenSet& layout);
virtual void handleClipboardRequest(); virtual void handleClipboardRequest();
virtual void handleClipboardAnnounce(bool available); virtual void handleClipboardAnnounce(bool available);
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
virtual void handleClipboardData(const char* data, int len); virtual void handleClipboardData(const char* data, int len);
// rfb::PixelBuffer callbacks // rfb::PixelBuffer callbacks

@ -308,6 +308,11 @@ Limit clipboard bytes to receive from clients in one transaction. Default 10,000
This many milliseconds must pass between clipboard actions. Default 1000. This many milliseconds must pass between clipboard actions. Default 1000.
. .
.TP .TP
.B \-DLP_Clip_types \fIa,b\fP
Allowed binary clipboard mimetypes, separated by commas. Default
chromium/x-web-custom-data,text/html,image/png
.
.TP
.B \-DLP_KeyRateLimit \fIkeys-per-second\fP .B \-DLP_KeyRateLimit \fIkeys-per-second\fP
Reject keyboard presses over this many per second. Default 0 (disabled). Reject keyboard presses over this many per second. Default 0 (disabled).
. .

@ -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,37 @@ 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") ||
!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 +198,8 @@ void vncExtensionInit(void)
vncAddExtension(); vncAddExtension();
if (!initialised)
parseClipTypes();
vncSelectionInit(); vncSelectionInit();
vlog.info("VNC extension running!"); vlog.info("VNC extension running!");
@ -333,6 +370,26 @@ void vncSendClipboardData(const char* data)
desktop[scr]->sendClipboardData(data); desktop[scr]->sendClipboardData(data);
} }
void vncSendBinaryClipboardData(const char* mime, const unsigned char *data,
const unsigned len)
{
for (int scr = 0; scr < vncGetScreenCount(); scr++)
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)
{ {
if (strlen(addr) == 0) { if (strlen(addr) == 0) {
@ -486,3 +543,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;
}

@ -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);
@ -63,6 +70,11 @@ void vncUpdateDesktopName(void);
void vncRequestClipboard(void); void vncRequestClipboard(void);
void vncAnnounceClipboard(int available); void vncAnnounceClipboard(int available);
void vncSendClipboardData(const char* data); 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);

@ -43,6 +43,12 @@
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 *xaBinclips;
static unsigned xaHtmlIndex, xaPngIndex;
static Bool htmlPngPresent;
static unsigned *mimeIndexesFromClient;
static unsigned numMimesFromClient;
static WindowPtr pWindow; static WindowPtr pWindow;
static Window wid; static Window wid;
@ -90,6 +96,29 @@ 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);
unsigned i;
mimeIndexesFromClient = calloc(dlp_num_mimetypes(), sizeof(unsigned));
numMimesFromClient = 0;
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];
@ -161,6 +190,66 @@ void vncHandleClipboardAnnounce(int available)
} }
} }
void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
{
if (num) {
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;
}
}
}
numMimesFromClient = valid;
LOG_DEBUG("Client sent %u mimes, %u were valid", num, valid);
} else {
struct VncDataTarget* next;
numMimesFromClient = 0;
if (pWindow == NULL)
return;
LOG_DEBUG("Remote binary clipboard lost, removing local ownership");
DeleteWindowFromAnySelections(pWindow);
/* Abort any pending transfer */
while (vncDataTargetHead != NULL) {
xEvent event;
event.u.u.type = SelectionNotify;
event.u.selectionNotify.time = vncDataTargetHead->time;
event.u.selectionNotify.requestor = vncDataTargetHead->requestor;
event.u.selectionNotify.selection = vncDataTargetHead->selection;
event.u.selectionNotify.target = vncDataTargetHead->target;
event.u.selectionNotify.property = None;
WriteEventsToClient(vncDataTargetHead->client, 1, &event);
next = vncDataTargetHead->next;
free(vncDataTargetHead);
vncDataTargetHead = next;
}
}
}
void vncHandleClipboardData(const char* data, int len) void vncHandleClipboardData(const char* data, int len)
{ {
struct VncDataTarget* next; struct VncDataTarget* next;
@ -277,6 +366,19 @@ 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,
@ -315,14 +417,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,6 +444,15 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
TRUE); TRUE);
if (rc != Success) if (rc != Success)
return rc; return rc;
} else if (clientHasBinaryAtom(target, &binatomidx)) {
const unsigned char *data;
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 { } else {
if (data == NULL) { if (data == NULL) {
struct VncDataTarget* vdt; struct VncDataTarget* vdt;
@ -379,7 +499,7 @@ 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 {
@ -483,6 +603,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)
@ -510,7 +645,8 @@ 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");
activeSelection = selection; activeSelection = selection;
vncAnnounceClipboard(TRUE); vncAnnounceClipboard(TRUE);
@ -520,6 +656,28 @@ static void vncHandleSelection(Atom selection, Atom target,
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;
Bool cleared = 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)) {
if (!cleared) {
vncClearBinaryClipboardData();
cleared = TRUE;
}
vncSelectionRequest(selection, xaBinclips[i]);
//break;
}
}
} }
} else if (target == xaSTRING) { } else if (target == xaSTRING) {
char* filtered; char* filtered;
@ -563,6 +721,25 @@ static void vncHandleSelection(Atom selection, Atom target,
vncSendClipboardData(filtered); vncSendClipboardData(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;
}
}
} }
} }

@ -26,6 +26,7 @@ void vncSelectionInit(void);
void vncHandleClipboardRequest(void); void vncHandleClipboardRequest(void);
void vncHandleClipboardAnnounce(int available); void vncHandleClipboardAnnounce(int available);
void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
void vncHandleClipboardData(const char* data, int len); void vncHandleClipboardData(const char* data, int len);
#ifdef __cplusplus #ifdef __cplusplus

Loading…
Cancel
Save