Initial binary clipboard support
This commit is contained in:
@@ -353,6 +353,25 @@ void SConnection::handleClipboardProvide(rdr::U32 flags,
|
||||
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()
|
||||
{
|
||||
writer()->writeQEMUKeyEvent();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/SMsgHandler.h>
|
||||
#include <rfb/SecurityServer.h>
|
||||
#include <vector>
|
||||
|
||||
namespace rfb {
|
||||
|
||||
@@ -81,6 +82,9 @@ namespace rfb {
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
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();
|
||||
|
||||
@@ -221,12 +225,19 @@ namespace rfb {
|
||||
|
||||
rdr::S32 getPreferredEncoding() { return preferredEncoding; }
|
||||
|
||||
struct binaryClipboard_t {
|
||||
char mime[32];
|
||||
std::vector<unsigned char> data;
|
||||
};
|
||||
|
||||
protected:
|
||||
void setState(stateEnum s) { state_ = s; }
|
||||
|
||||
void setReader(SMsgReader *r) { reader_ = r; }
|
||||
void setWriter(SMsgWriter *w) { writer_ = w; }
|
||||
|
||||
std::vector<binaryClipboard_t> binaryClipboard;
|
||||
|
||||
private:
|
||||
void writeFakeColourMap(void);
|
||||
|
||||
|
||||
@@ -89,6 +89,9 @@ namespace rfb {
|
||||
// access the actual data.
|
||||
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
|
||||
// the clipboard data as a result of a previous call to
|
||||
// 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);
|
||||
}
|
||||
|
||||
void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32])
|
||||
{
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace rfb {
|
||||
virtual void enableContinuousUpdates(bool enable,
|
||||
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,
|
||||
const rdr::U32* lengths);
|
||||
virtual void handleClipboardRequest(rdr::U32 flags);
|
||||
@@ -62,6 +63,9 @@ namespace rfb {
|
||||
virtual void handleClipboardProvide(rdr::U32 flags,
|
||||
const size_t* lengths,
|
||||
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 handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;
|
||||
|
||||
@@ -83,6 +83,9 @@ void SMsgReader::readMsg()
|
||||
case msgTypeFrameStats:
|
||||
readFrameStats();
|
||||
break;
|
||||
case msgTypeBinaryClipboard:
|
||||
readBinaryClipboard();
|
||||
break;
|
||||
case msgTypeKeyEvent:
|
||||
readKeyEvent();
|
||||
break;
|
||||
@@ -249,6 +252,44 @@ void SMsgReader::readClientCutText()
|
||||
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)
|
||||
{
|
||||
rdr::U32 flags;
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace rfb {
|
||||
void readExtendedClipboard(rdr::S32 len);
|
||||
void readRequestStats();
|
||||
void readFrameStats();
|
||||
void readBinaryClipboard();
|
||||
|
||||
void readQEMUMessage();
|
||||
void readQEMUKeyEvent();
|
||||
|
||||
@@ -200,6 +200,24 @@ void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
|
||||
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)
|
||||
{
|
||||
startMsg(msgTypeStats);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <rdr/types.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/ScreenSet.h>
|
||||
#include <rfb/SConnection.h>
|
||||
#include <vector>
|
||||
|
||||
namespace rdr { class OutStream; }
|
||||
|
||||
@@ -62,6 +64,7 @@ namespace rfb {
|
||||
void writeClipboardNotify(rdr::U32 flags);
|
||||
void writeClipboardProvide(rdr::U32 flags, const size_t* lengths,
|
||||
const rdr::U8* const* data);
|
||||
void writeBinaryClipboard(const std::vector<SConnection::binaryClipboard_t> &b);
|
||||
|
||||
void writeStats(const char* str, int len);
|
||||
|
||||
|
||||
@@ -171,6 +171,10 @@ rfb::StringParameter rfb::Server::DLP_Region
|
||||
("DLP_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
|
||||
("DLP_RegionAllowClick",
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace rfb {
|
||||
static IntParameter DLP_KeyRateLimit;
|
||||
static StringParameter DLP_ClipLog;
|
||||
static StringParameter DLP_Region;
|
||||
static StringParameter DLP_Clip_Types;
|
||||
static BoolParameter DLP_RegionAllowClick;
|
||||
static BoolParameter DLP_RegionAllowRelease;
|
||||
static IntParameter jpegVideoQuality;
|
||||
|
||||
@@ -57,7 +57,8 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
inProcessMessages(false),
|
||||
pendingSyncFence(false), syncFence(false), fenceFlags(0),
|
||||
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),
|
||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||
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)
|
||||
{
|
||||
try {
|
||||
@@ -1027,6 +1080,13 @@ void VNCSConnectionST::handleClipboardAnnounce(bool 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)
|
||||
{
|
||||
if (!(accessRights & AccessCutText)) return;
|
||||
@@ -1089,6 +1149,8 @@ bool VNCSConnectionST::handleTimeout(Timer* t)
|
||||
writeFramebufferUpdate();
|
||||
else if (t == &kbdLogTimer)
|
||||
flushKeylog(sock->getPeerAddress());
|
||||
else if (t == &binclipTimer)
|
||||
writeBinaryClipboard();
|
||||
} catch (rdr::Exception& e) {
|
||||
close(e.str());
|
||||
}
|
||||
@@ -1446,6 +1508,10 @@ void VNCSConnectionST::writeDataUpdate()
|
||||
requested.clear();
|
||||
}
|
||||
|
||||
void VNCSConnectionST::writeBinaryClipboard()
|
||||
{
|
||||
writer()->writeBinaryClipboard(binaryClipboard);
|
||||
}
|
||||
|
||||
void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
|
||||
{
|
||||
|
||||
@@ -80,6 +80,11 @@ namespace rfb {
|
||||
void requestClipboardOrClose();
|
||||
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
|
||||
// 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);
|
||||
virtual void handleClipboardRequest();
|
||||
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 supportsLocalCursor();
|
||||
virtual void supportsFence();
|
||||
@@ -260,6 +266,8 @@ namespace rfb {
|
||||
void writeNoDataUpdate();
|
||||
void writeDataUpdate();
|
||||
|
||||
void writeBinaryClipboard();
|
||||
|
||||
void screenLayoutChange(rdr::U16 reason);
|
||||
void setCursor();
|
||||
void setCursorPos();
|
||||
@@ -282,6 +290,7 @@ namespace rfb {
|
||||
Timer congestionTimer;
|
||||
Timer losslessTimer;
|
||||
Timer kbdLogTimer;
|
||||
Timer binclipTimer;
|
||||
|
||||
VNCServerST* server;
|
||||
SimpleUpdateTracker updates;
|
||||
|
||||
@@ -557,6 +557,33 @@ void VNCServerST::sendClipboardData(const char* data)
|
||||
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()
|
||||
{
|
||||
std::list<VNCSConnectionST*>::iterator ci, ci_next;
|
||||
@@ -1218,6 +1245,14 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
|
||||
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,
|
||||
const char* data, int len)
|
||||
{
|
||||
|
||||
@@ -99,6 +99,11 @@ namespace rfb {
|
||||
virtual void requestClipboard();
|
||||
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 ®ion);
|
||||
virtual void add_copied(const Region &dest, const Point &delta);
|
||||
virtual void setCursor(int width, int height, const Point& hotspot,
|
||||
@@ -193,6 +198,8 @@ namespace rfb {
|
||||
|
||||
void handleClipboardRequest(VNCSConnectionST* client);
|
||||
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);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace rfb {
|
||||
// kasm
|
||||
const int msgTypeStats = 178;
|
||||
const int msgTypeRequestFrameStats = 179;
|
||||
const int msgTypeBinaryClipboard = 180;
|
||||
|
||||
const int msgTypeServerFence = 248;
|
||||
|
||||
@@ -49,6 +50,7 @@ namespace rfb {
|
||||
// kasm
|
||||
const int msgTypeRequestStats = 178;
|
||||
const int msgTypeFrameStats = 179;
|
||||
//const int msgTypeBinaryClipboard = 180;
|
||||
|
||||
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()
|
||||
{
|
||||
server->bell();
|
||||
@@ -479,6 +510,11 @@ void XserverDesktop::handleClipboardAnnounce(bool available)
|
||||
vncHandleClipboardAnnounce(available);
|
||||
}
|
||||
|
||||
void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32])
|
||||
{
|
||||
vncHandleClipboardAnnounceBinary(num, mimes);
|
||||
}
|
||||
|
||||
void XserverDesktop::handleClipboardData(const char* data_, int len)
|
||||
{
|
||||
vncHandleClipboardData(data_, len);
|
||||
|
||||
@@ -63,6 +63,11 @@ public:
|
||||
void requestClipboard();
|
||||
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 setLEDState(unsigned int state);
|
||||
void setDesktopName(const char* name);
|
||||
@@ -96,6 +101,7 @@ public:
|
||||
const rfb::ScreenSet& layout);
|
||||
virtual void handleClipboardRequest();
|
||||
virtual void handleClipboardAnnounce(bool available);
|
||||
virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
|
||||
virtual void handleClipboardData(const char* data, int len);
|
||||
|
||||
// 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.
|
||||
.
|
||||
.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
|
||||
Reject keyboard presses over this many per second. Default 0 (disabled).
|
||||
.
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/Logger_stdio.h>
|
||||
@@ -64,6 +66,8 @@ int vncFbstride[MAXSCREENS];
|
||||
|
||||
int vncInetdSock = -1;
|
||||
|
||||
static std::vector<dlp_mimetype_t> dlp_mimetypes;
|
||||
|
||||
struct CaseInsensitiveCompare {
|
||||
bool operator() (const std::string &a, const std::string &b) const {
|
||||
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)
|
||||
{
|
||||
if (vncExtGeneration == vncGetServerGeneration()) {
|
||||
@@ -163,6 +198,8 @@ void vncExtensionInit(void)
|
||||
|
||||
vncAddExtension();
|
||||
|
||||
if (!initialised)
|
||||
parseClipTypes();
|
||||
vncSelectionInit();
|
||||
|
||||
vlog.info("VNC extension running!");
|
||||
@@ -333,6 +370,26 @@ void vncSendClipboardData(const char* 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)
|
||||
{
|
||||
if (strlen(addr) == 0) {
|
||||
@@ -486,3 +543,13 @@ int vncOverrideParam(const char *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 int vncFbstride[];
|
||||
|
||||
struct dlp_mimetype_t {
|
||||
char mime[32];
|
||||
};
|
||||
|
||||
unsigned dlp_num_mimetypes();
|
||||
const char *dlp_get_mimetype(const unsigned i);
|
||||
|
||||
extern int vncInetdSock;
|
||||
|
||||
void vncExtensionInit(void);
|
||||
@@ -63,6 +70,11 @@ void vncUpdateDesktopName(void);
|
||||
void vncRequestClipboard(void);
|
||||
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);
|
||||
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
|
||||
static Atom xaPRIMARY, xaCLIPBOARD;
|
||||
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 Window wid;
|
||||
@@ -90,6 +96,29 @@ void vncSelectionInit(void)
|
||||
xaTEXT = MakeAtom("TEXT", 4, 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
|
||||
* override the relevant handlers. */
|
||||
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)
|
||||
{
|
||||
struct VncDataTarget* next;
|
||||
@@ -277,6 +366,19 @@ static int vncOwnSelection(Atom selection)
|
||||
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,
|
||||
Atom target, Atom property,
|
||||
Window requestor, CARD32 time,
|
||||
@@ -315,14 +417,23 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
|
||||
realProperty = target;
|
||||
|
||||
/* FIXME: MULTIPLE target */
|
||||
unsigned binatomidx;
|
||||
|
||||
if (target == xaTARGETS) {
|
||||
Atom targets[] = { xaTARGETS, xaTIMESTAMP,
|
||||
xaSTRING, xaTEXT, xaUTF8_STRING };
|
||||
Atom targets[5 + numMimesFromClient];
|
||||
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,
|
||||
XA_ATOM, 32, PropModeReplace,
|
||||
sizeof(targets)/sizeof(targets[0]),
|
||||
5 + numMimesFromClient,
|
||||
targets, TRUE);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
@@ -333,6 +444,15 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
|
||||
TRUE);
|
||||
if (rc != Success)
|
||||
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 {
|
||||
if (data == NULL) {
|
||||
struct VncDataTarget* vdt;
|
||||
@@ -379,7 +499,7 @@ static int vncConvertSelection(ClientPtr client, Atom selection,
|
||||
} else if (target == xaUTF8_STRING) {
|
||||
rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
|
||||
xaUTF8_STRING, 8, PropModeReplace,
|
||||
len, data, TRUE);
|
||||
len, (char *) data, TRUE);
|
||||
if (rc != Success)
|
||||
return rc;
|
||||
} else {
|
||||
@@ -483,6 +603,21 @@ static Bool vncHasAtom(Atom atom, const Atom list[], size_t size)
|
||||
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,
|
||||
Atom property, Atom requestor,
|
||||
TimeStamp time)
|
||||
@@ -510,7 +645,8 @@ static void vncHandleSelection(Atom selection, Atom target,
|
||||
|
||||
if (probing) {
|
||||
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");
|
||||
activeSelection = selection;
|
||||
vncAnnounceClipboard(TRUE);
|
||||
@@ -520,6 +656,28 @@ static void vncHandleSelection(Atom selection, Atom target,
|
||||
vncSelectionRequest(selection, xaUTF8_STRING);
|
||||
else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
|
||||
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) {
|
||||
char* filtered;
|
||||
@@ -563,6 +721,25 @@ static void vncHandleSelection(Atom selection, Atom target,
|
||||
vncSendClipboardData(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 vncHandleClipboardAnnounce(int available);
|
||||
void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]);
|
||||
void vncHandleClipboardData(const char* data, int len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user