Initial binary clipboard support

This commit is contained in:
Lauri Kasanen
2021-09-16 15:49:42 +03:00
parent 0cb2c0ba9f
commit 53c8de789f
23 changed files with 547 additions and 6 deletions

View File

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

View File

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

View File

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

View File

@@ -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()
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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",

View File

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

View File

@@ -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)
{

View File

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

View File

@@ -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)
{

View File

@@ -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 &region);
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:

View File

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