diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index bd1ea45..15b22d2 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -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(); diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 811ba04..68b50c7 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -28,6 +28,7 @@ #include #include #include +#include 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 data; + }; + protected: void setState(stateEnum s) { state_ = s; } void setReader(SMsgReader *r) { reader_ = r; } void setWriter(SMsgWriter *w) { writer_ = w; } + std::vector binaryClipboard; + private: void writeFakeColourMap(void); diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index ec833c3..c06ae0b 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -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 diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx index 29c33ea..74bd194 100644 --- a/common/rfb/SMsgHandler.cxx +++ b/common/rfb/SMsgHandler.cxx @@ -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() { } diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index d2fe4af..8d29b50 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -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; diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index de5e1b3..84f9b5b 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -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; diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h index a9b09cc..8a1eb34 100644 --- a/common/rfb/SMsgReader.h +++ b/common/rfb/SMsgReader.h @@ -58,6 +58,7 @@ namespace rfb { void readExtendedClipboard(rdr::S32 len); void readRequestStats(); void readFrameStats(); + void readBinaryClipboard(); void readQEMUMessage(); void readQEMUKeyEvent(); diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index a7c12f4..8127716 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -200,6 +200,24 @@ void SMsgWriter::writeClipboardProvide(rdr::U32 flags, endMsg(); } +void SMsgWriter::writeBinaryClipboard(const std::vector &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); diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 0313bcc..e28f0e1 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include 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 &b); void writeStats(const char* str, int len); diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 28ce777..af3ce3e 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -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", diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index 766eda0..70075bf 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -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; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index bd24229..354697d 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -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) { diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 86c99c6..76a0911 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -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; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 3e87892..656441a 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -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::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::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::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) { diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 26c9ef6..0fe3a08 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -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: diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h index 5070c3f..ea8e007 100644 --- a/common/rfb/msgTypes.h +++ b/common/rfb/msgTypes.h @@ -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; diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 1e9d663..5f7b20c 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -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); diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 014a48e..0bcdb4b 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -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 diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index f3a6ac6..23007e3 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -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). . diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 6b60c0a..943b15c 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -18,10 +18,12 @@ */ #include +#include #include #include #include +#include #include #include @@ -64,6 +66,8 @@ int vncFbstride[MAXSCREENS]; int vncInetdSock = -1; +static std::vector 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; +} diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 943537d..fd01ab4 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -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); diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 21a2a61..4d708b1 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -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; + } + } } } diff --git a/unix/xserver/hw/vnc/vncSelection.h b/unix/xserver/hw/vnc/vncSelection.h index 337f9bf..39cb5a9 100644 --- a/unix/xserver/hw/vnc/vncSelection.h +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -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