diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index bd1ea45..5567a96 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -284,74 +284,26 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings) } SMsgHandler::setEncodings(nEncodings, encodings); - - if (cp.supportsExtendedClipboard) { - rdr::U32 sizes[] = { 0 }; - writer()->writeClipboardCaps(rfb::clipboardUTF8 | - rfb::clipboardRequest | - rfb::clipboardPeek | - rfb::clipboardNotify | - rfb::clipboardProvide, - sizes); - } } -void SConnection::clientCutText(const char* str, int len) +void SConnection::clearBinaryClipboard() { - hasLocalClipboard = false; - - strFree(clientClipboard); - clientClipboard = NULL; - - clientClipboard = latin1ToUTF8(str); - - handleClipboardAnnounce(true); + binaryClipboard.clear(); } -void SConnection::handleClipboardRequest(rdr::U32 flags) +void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len) { - if (!(flags & rfb::clipboardUTF8)) - return; - if (!hasLocalClipboard) - return; - handleClipboardRequest(); + 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::handleClipboardPeek(rdr::U32 flags) -{ - if (!hasLocalClipboard) - return; - if (cp.clipboardFlags() & rfb::clipboardNotify) - writer()->writeClipboardNotify(rfb::clipboardUTF8); -} - -void SConnection::handleClipboardNotify(rdr::U32 flags) -{ - strFree(clientClipboard); - clientClipboard = NULL; - - if (flags & rfb::clipboardUTF8) { - handleClipboardAnnounce(true); - hasLocalClipboard = false; - } else { - handleClipboardAnnounce(false); - } -} - -void SConnection::handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) -{ - if (!(flags & rfb::clipboardUTF8)) - return; - - strFree(clientClipboard); - clientClipboard = NULL; - - clientClipboard = convertLF((const char*)data[0], lengths[0]); - - handleClipboardData(clientClipboard, strlen(clientClipboard)); -} void SConnection::supportsQEMUKeyEvent() { @@ -445,56 +397,13 @@ void SConnection::enableContinuousUpdates(bool enable, { } -void SConnection::handleClipboardRequest() -{ -} - void SConnection::handleClipboardAnnounce(bool available) { } -void SConnection::handleClipboardData(const char* data, int len) -{ -} - -void SConnection::requestClipboard() -{ - if (clientClipboard != NULL) { - handleClipboardData(clientClipboard, strlen(clientClipboard)); - return; - } - - if (cp.supportsExtendedClipboard && - (cp.clipboardFlags() & rfb::clipboardRequest)) - writer()->writeClipboardRequest(rfb::clipboardUTF8); -} - void SConnection::announceClipboard(bool available) { hasLocalClipboard = available; - - if (cp.supportsExtendedClipboard && - (cp.clipboardFlags() & rfb::clipboardNotify)) - writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0); - else { - if (available) - handleClipboardRequest(); - } -} - -void SConnection::sendClipboardData(const char* data, int len) -{ - if (cp.supportsExtendedClipboard && - (cp.clipboardFlags() & rfb::clipboardProvide)) { - CharArray filtered(convertCRLF(data)); - size_t sizes[1] = { strlen(filtered.buf) + 1 }; - const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf }; - writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data); - } else { - CharArray latin1(utf8ToLatin1(data)); - - writer()->writeServerCutText(latin1.buf, strlen(latin1.buf)); - } } void SConnection::writeFakeColourMap(void) diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 811ba04..8b5b3de 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace rfb { @@ -73,14 +74,9 @@ namespace rfb { virtual void setEncodings(int nEncodings, const rdr::S32* encodings); - virtual void clientCutText(const char* str, int len); - - virtual void handleClipboardRequest(rdr::U32 flags); - virtual void handleClipboardPeek(rdr::U32 flags); - virtual void handleClipboardNotify(rdr::U32 flags); - virtual void handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data); + virtual void clearBinaryClipboard(); + virtual void addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len); virtual void supportsQEMUKeyEvent(); @@ -127,25 +123,11 @@ namespace rfb { virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); - // handleClipboardRequest() is called whenever the client requests - // the server to send over its clipboard data. It will only be - // called after the server has first announced a clipboard change - // via announceClipboard(). - virtual void handleClipboardRequest(); - // handleClipboardAnnounce() is called to indicate a change in the // clipboard on the client. Call requestClipboard() to access the // actual data. virtual void handleClipboardAnnounce(bool available); - // handleClipboardData() is called when the client has sent over - // the clipboard data as a result of a previous call to - // requestClipboard(). Note that this function might never be - // called if the clipboard data was no longer available when the - // client received the request. - virtual void handleClipboardData(const char* data, int len); - - virtual void add_changed_all() {} // setAccessRights() allows a security package to limit the access rights @@ -166,22 +148,11 @@ namespace rfb { // Other methods - // requestClipboard() will result in a request to the client to - // transfer its clipboard data. A call to handleClipboardData() - // will be made once the data is available. - virtual void requestClipboard(); - // announceClipboard() informs the client of changes to the // clipboard on the server. The client may later request the // clipboard data via handleClipboardRequest(). virtual void announceClipboard(bool available); - // sendClipboardData() transfers the clipboard data to the client - // and should be called whenever the client has requested the - // clipboard via handleClipboardRequest(). - virtual void sendClipboardData(const char* data, int len); - - // authenticated() returns true if the client has authenticated // successfully. bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || @@ -221,12 +192,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..5696cfd 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -78,23 +78,13 @@ namespace rfb { // the relevant RFB protocol messages from clients. // See InputHandler for method signatures. - // handleClipboardRequest() is called whenever a client requests - // the server to send over its clipboard data. It will only be - // called after the server has first announced a clipboard change - // via VNCServer::announceClipboard(). - virtual void handleClipboardRequest() {} - // handleClipboardAnnounce() is called to indicate a change in the // clipboard on a client. Call VNCServer::requestClipboard() to // access the actual data. virtual void handleClipboardAnnounce(bool __unused_attr available) {} - // 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 - // never be called if the clipboard data was no longer available - // when the client received the request. - virtual void handleClipboardData(const char* __unused_attr data, int len __unused_attr) {} + virtual void handleClipboardAnnounceBinary(const unsigned __unused_attr num, + const char __unused_attr mimes[][32]) {} protected: virtual ~SDesktop() {} diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx index 29c33ea..07499bc 100644 --- a/common/rfb/SMsgHandler.cxx +++ b/common/rfb/SMsgHandler.cxx @@ -64,26 +64,16 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings) supportsQEMUKeyEvent(); } -void SMsgHandler::handleClipboardCaps(rdr::U32 flags, const rdr::U32* lengths) -{ - cp.setClipboardCaps(flags, lengths); -} - -void SMsgHandler::handleClipboardRequest(rdr::U32 flags) +void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32]) { } -void SMsgHandler::handleClipboardPeek(rdr::U32 flags) +void SMsgHandler::clearBinaryClipboard() { } -void SMsgHandler::handleClipboardNotify(rdr::U32 flags) -{ -} - -void SMsgHandler::handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) +void SMsgHandler::addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len) { } diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index d2fe4af..d9d852a 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -54,14 +54,10 @@ namespace rfb { virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h) = 0; - virtual void handleClipboardCaps(rdr::U32 flags, - const rdr::U32* lengths); - virtual void handleClipboardRequest(rdr::U32 flags); - virtual void handleClipboardPeek(rdr::U32 flags); - virtual void handleClipboardNotify(rdr::U32 flags); - virtual void handleClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); + 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 b9d3491..3719756 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -35,8 +35,6 @@ using namespace rfb; static LogWriter vlog("SMsgReader"); -static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024); - SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_) : handler(handler_), is(is_) { @@ -83,6 +81,9 @@ void SMsgReader::readMsg() case msgTypeFrameStats: readFrameStats(); break; + case msgTypeBinaryClipboard: + readBinaryClipboard(); + break; case msgTypeKeyEvent: readKeyEvent(); break; @@ -240,109 +241,54 @@ void SMsgReader::readClientCutText() readExtendedClipboard(slen); return; } - if (len > (size_t)maxCutText) { - is->skip(len); - vlog.error("Cut text too long (%d bytes) - ignoring", len); - return; + is->skip(len); + vlog.error("Client sent old cuttext msg, ignoring"); +} + +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++; } - CharArray ca(len+1); - ca.buf[len] = 0; - is->readBytes(ca.buf, len); - handler->clientCutText(ca.buf, len); + + handler->handleClipboardAnnounceBinary(valid, tmpmimes); } void SMsgReader::readExtendedClipboard(rdr::S32 len) { - rdr::U32 flags; - rdr::U32 action; - if (len < 4) throw Exception("Invalid extended clipboard message"); - if (len > maxCutText) { - vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len); - is->skip(len); - return; - } - - flags = is->readU32(); - action = flags & clipboardActionMask; - - if (action & clipboardCaps) { - int i; - size_t num; - rdr::U32 lengths[16]; - - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - num++; - } - - if (len < (rdr::S32)(4 + 4*num)) - throw Exception("Invalid extended clipboard message"); - - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - lengths[num++] = is->readU32(); - } - - handler->handleClipboardCaps(flags, lengths); - } else if (action == clipboardProvide) { - rdr::ZlibInStream zis; - - int i; - size_t num; - size_t lengths[16]; - rdr::U8* buffers[16]; - - zis.setUnderlying(is, len - 4); - - num = 0; - for (i = 0;i < 16;i++) { - if (!(flags & 1 << i)) - continue; - - lengths[num] = zis.readU32(); - if (lengths[num] > (size_t)maxCutText) { - vlog.error("Extended clipboard data too long (%d bytes) - ignoring", - (unsigned)lengths[num]); - zis.skip(lengths[num]); - flags &= ~(1 << i); - continue; - } - - buffers[num] = new rdr::U8[lengths[num]]; - zis.readBytes(buffers[num], lengths[num]); - num++; - } - - zis.flushUnderlying(); - zis.setUnderlying(NULL, 0); - - handler->handleClipboardProvide(flags, lengths, buffers); - - num = 0; - for (i = 0;i < 16;i++) { - if (!(flags & 1 << i)) - continue; - delete [] buffers[num++]; - } - } else { - switch (action) { - case clipboardRequest: - handler->handleClipboardRequest(flags); - break; - case clipboardPeek: - handler->handleClipboardPeek(flags); - break; - case clipboardNotify: - handler->handleClipboardNotify(flags); - break; - default: - throw Exception("Invalid extended clipboard action"); - } - } + vlog.error("Client sent old cuttext msg, ignoring"); + is->skip(len); } void SMsgReader::readRequestStats() 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..a0a9df0 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -85,118 +85,21 @@ void SMsgWriter::writeBell() endMsg(); } -void SMsgWriter::writeServerCutText(const char* str, int len) +void SMsgWriter::writeBinaryClipboard(const std::vector &b) { - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeU32(len); - os->writeBytes(str, len); - endMsg(); -} + startMsg(msgTypeBinaryClipboard); -void SMsgWriter::writeClipboardCaps(rdr::U32 caps, - const rdr::U32* lengths) -{ - size_t i, count; + 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); - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - - count = 0; - for (i = 0;i < 16;i++) { - if (caps & (1 << i)) - count++; + os->writeU32(b[i].data.size()); + os->writeBytes(&b[i].data[0], b[i].data.size()); } - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-(4 + 4 * count)); - - os->writeU32(caps | clipboardCaps); - - count = 0; - for (i = 0;i < 16;i++) { - if (caps & (1 << i)) - os->writeU32(lengths[count++]); - } - - endMsg(); -} - -void SMsgWriter::writeClipboardRequest(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardRequest)) - throw Exception("Client does not support clipboard \"request\" action"); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardRequest); - endMsg(); -} - -void SMsgWriter::writeClipboardPeek(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardPeek)) - throw Exception("Client does not support clipboard \"peek\" action"); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardPeek); - endMsg(); -} - -void SMsgWriter::writeClipboardNotify(rdr::U32 flags) -{ - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardNotify)) - throw Exception("Client does not support clipboard \"notify\" action"); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-4); - os->writeU32(flags | clipboardNotify); - endMsg(); -} - -void SMsgWriter::writeClipboardProvide(rdr::U32 flags, - const size_t* lengths, - const rdr::U8* const* data) -{ - rdr::MemOutStream mos; - rdr::ZlibOutStream zos; - - int i, count; - - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - if (!(cp->clipboardFlags() & clipboardProvide)) - throw Exception("Client does not support clipboard \"provide\" action"); - - zos.setUnderlying(&mos); - - count = 0; - for (i = 0;i < 16;i++) { - if (!(flags & (1 << i))) - continue; - zos.writeU32(lengths[count]); - zos.writeBytes(data[count], lengths[count]); - count++; - } - - zos.flush(); - - startMsg(msgTypeServerCutText); - os->pad(3); - os->writeS32(-(4 + mos.length())); - os->writeU32(flags | clipboardProvide); - os->writeBytes(mos.data(), mos.length()); endMsg(); } diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 0313bcc..c72012e 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include namespace rdr { class OutStream; } @@ -54,14 +56,8 @@ namespace rfb { // writeBell() and writeServerCutText() do the obvious thing. void writeBell(); - void writeServerCutText(const char* str, int len); - void writeClipboardCaps(rdr::U32 caps, const rdr::U32* lengths); - void writeClipboardRequest(rdr::U32 flags); - void writeClipboardPeek(rdr::U32 flags); - void writeClipboardNotify(rdr::U32 flags); - void writeClipboardProvide(rdr::U32 flags, const size_t* lengths, - const rdr::U8* const* data); + void 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..1d568c1 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -149,15 +149,15 @@ rfb::IntParameter rfb::Server::webpVideoQuality rfb::IntParameter rfb::Server::DLP_ClipSendMax ("DLP_ClipSendMax", "Limit clipboard bytes to send to clients in one transaction", - 10000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_ClipAcceptMax ("DLP_ClipAcceptMax", "Limit clipboard bytes to receive from clients in one transaction", - 10000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_ClipDelay ("DLP_ClipDelay", "This many milliseconds must pass between clipboard actions", - 1000, 0, INT_MAX); + 0, 0, INT_MAX); rfb::IntParameter rfb::Server::DLP_KeyRateLimit ("DLP_KeyRateLimit", "Reject keyboard presses over this many per second", @@ -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_ClipTypes", + "Allowed binary clipboard mimetypes", + "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 10e265c..fa85a70 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), @@ -413,18 +414,6 @@ static void keylog(unsigned keysym, const char *client) { flushKeylog(client); } -void VNCSConnectionST::requestClipboardOrClose() -{ - try { - if (!(accessRights & AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; - if (state() != RFBSTATE_NORMAL) return; - requestClipboard(); - } catch(rdr::Exception& e) { - close(e.str()); - } -} - void VNCSConnectionST::announceClipboardOrClose(bool available) { try { @@ -437,29 +426,51 @@ void VNCSConnectionST::announceClipboardOrClose(bool available) } } -void VNCSConnectionST::sendClipboardDataOrClose(const char* data) +void VNCSConnectionST::clearBinaryClipboardData() +{ + clearBinaryClipboard(); +} + +void VNCSConnectionST::sendBinaryClipboardDataOrClose(const char* mime, + const unsigned char *data, + const unsigned len) { try { 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 clipboard, too soon", + 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; } - int len = strlen(data); - const int origlen = len; - if (rfb::Server::DLP_ClipSendMax && len > rfb::Server::DLP_ClipSendMax) - len = rfb::Server::DLP_ClipSendMax; - cliplog(data, len, origlen, "sent", sock->getPeerAddress()); + + cliplog((const char *) data, len, len, "sent", sock->getPeerAddress()); if (state() != RFBSTATE_NORMAL) return; - sendClipboardData(data, len); - gettimeofday(&lastClipboardOp, NULL); + + addBinaryClipboard(mime, data, len); + binclipTimer.start(100); } 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 { @@ -1014,12 +1025,6 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, } } -void VNCSConnectionST::handleClipboardRequest() -{ - if (!(accessRights & AccessCutText)) return; - server->handleClipboardRequest(this); -} - void VNCSConnectionST::handleClipboardAnnounce(bool available) { if (!(accessRights & AccessCutText)) return; @@ -1027,25 +1032,13 @@ void VNCSConnectionST::handleClipboardAnnounce(bool available) server->handleClipboardAnnounce(this, available); } -void VNCSConnectionST::handleClipboardData(const char* data, int len) +void VNCSConnectionST::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { if (!(accessRights & AccessCutText)) return; if (!rfb::Server::acceptCutText) return; - if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { - vlog.info("DLP: client %s: refused to receive clipboard, too soon", - sock->getPeerAddress()); - return; - } - const int origlen = len; - if (rfb::Server::DLP_ClipAcceptMax && len > rfb::Server::DLP_ClipAcceptMax) - len = rfb::Server::DLP_ClipAcceptMax; - cliplog(data, len, origlen, "received", sock->getPeerAddress()); - - gettimeofday(&lastClipboardOp, NULL); - server->handleClipboardData(this, data, len); + server->handleClipboardAnnounceBinary(this, num, mimes); } - // supportsLocalCursor() is called whenever the status of // cp.supportsLocalCursor has changed. If the client does now support local // cursor, we make sure that the old server-side rendered cursor is cleaned up @@ -1089,6 +1082,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 +1441,18 @@ void VNCSConnectionST::writeDataUpdate() requested.clear(); } +void VNCSConnectionST::writeBinaryClipboard() +{ + if (msSince(&lastClipboardOp) < (unsigned) rfb::Server::DLP_ClipDelay) { + vlog.info("DLP: client %s: refused to send binary clipboard, too soon", + sock->getPeerAddress()); + return; + } + + writer()->writeBinaryClipboard(binaryClipboard); + + gettimeofday(&lastClipboardOp, NULL); +} void VNCSConnectionST::screenLayoutChange(rdr::U16 reason) { diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 4262337..63ce49c 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -77,9 +77,12 @@ namespace rfb { void bellOrClose(); void setDesktopNameOrClose(const char *name); void setLEDStateOrClose(unsigned int state); - 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 @@ -212,9 +215,8 @@ namespace rfb { virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void enableContinuousUpdates(bool enable, int x, int y, int w, int h); - virtual void handleClipboardRequest(); virtual void handleClipboardAnnounce(bool available); - virtual void handleClipboardData(const char* data, int len); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); virtual void supportsLocalCursor(); virtual void supportsFence(); virtual void supportsContinuousUpdates(); @@ -260,6 +262,8 @@ namespace rfb { void writeNoDataUpdate(); void writeDataUpdate(); + void writeBinaryClipboard(); + void screenLayoutChange(rdr::U16 reason); void setCursor(); void setCursorPos(); @@ -282,6 +286,7 @@ namespace rfb { Timer congestionTimer; Timer losslessTimer; Timer kbdLogTimer; + Timer binclipTimer; VNCServerST* server; SimpleUpdateTracker updates; diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 54e3504..33b0533 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -52,22 +52,11 @@ namespace rfb { // getPixelBuffer() returns a pointer to the PixelBuffer object. virtual PixelBuffer* getPixelBuffer() const = 0; - // requestClipboard() will result in a request to a client to - // transfer its clipboard data. A call to - // SDesktop::handleClipboardData() will be made once the data is - // available. - virtual void requestClipboard() = 0; - // announceClipboard() informs all clients of changes to the // clipboard on the server. A client may later request the // clipboard data via SDesktop::handleClipboardRequest(). virtual void announceClipboard(bool available) = 0; - // sendClipboardData() transfers the clipboard data to a client - // and should be called whenever a client has requested the - // clipboard via SDesktop::handleClipboardRequest(). - virtual void sendClipboardData(const char* data) = 0; - // bell() tells the server that it should make all clients make a bell sound. virtual void bell() = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 3e87892..7807feb 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -518,14 +518,6 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout) } } -void VNCServerST::requestClipboard() -{ - if (clipboardClient == NULL) - return; - - clipboardClient->requestClipboard(); -} - void VNCServerST::announceClipboard(bool available) { std::list::iterator ci, ci_next; @@ -541,20 +533,31 @@ void VNCServerST::announceClipboard(bool available) } } -void VNCServerST::sendClipboardData(const char* data) +void VNCServerST::sendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len) { std::list::iterator ci, ci_next; - - if (strchr(data, '\r') != NULL) - throw Exception("Invalid carriage return in clipboard data"); - - for (ci = clipboardRequestors.begin(); - ci != clipboardRequestors.end(); ci = ci_next) { + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; - (*ci)->sendClipboardDataOrClose(data); + (*ci)->sendBinaryClipboardDataOrClose(mime, data, len); } +} - clipboardRequestors.clear(); +void VNCServerST::getBinaryClipboardData(const char* mime, const unsigned char **data, + unsigned *len) +{ + if (!clipboardClient) + return; + clipboardClient->getBinaryClipboardData(mime, data, len); +} + +void VNCServerST::clearBinaryClipboardData() +{ + std::list::iterator ci, ci_next; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->clearBinaryClipboardData(); + } } void VNCServerST::bell() @@ -1198,13 +1201,6 @@ bool VNCServerST::getComparerState() return false; } -void VNCServerST::handleClipboardRequest(VNCSConnectionST* client) -{ - clipboardRequestors.push_back(client); - if (clipboardRequestors.size() == 1) - desktop->handleClipboardRequest(); -} - void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, bool available) { @@ -1218,11 +1214,10 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, desktop->handleClipboardAnnounce(available); } -void VNCServerST::handleClipboardData(VNCSConnectionST* client, - const char* data, int len) +void VNCServerST::handleClipboardAnnounceBinary(VNCSConnectionST* client, + const unsigned num, + const char mimes[][32]) { - if (client != clipboardClient) - return; - desktop->handleClipboardData(data, len); + clipboardClient = client; + desktop->handleClipboardAnnounceBinary(num, mimes); } - diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 26c9ef6..0eff2d1 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -96,9 +96,12 @@ namespace rfb { virtual void setPixelBuffer(PixelBuffer* pb); virtual void setScreenLayout(const ScreenSet& layout); virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; } - 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, @@ -191,9 +194,9 @@ namespace rfb { void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; } - void handleClipboardRequest(VNCSConnectionST* client); void handleClipboardAnnounce(VNCSConnectionST* client, bool available); - void handleClipboardData(VNCSConnectionST* client, const char* data, int len); + void handleClipboardAnnounceBinary(VNCSConnectionST* client, const unsigned num, + const char mimes[][32]); protected: 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/common/rfb/scale_sse2.cxx b/common/rfb/scale_sse2.cxx index e4c717b..d4110a6 100644 --- a/common/rfb/scale_sse2.cxx +++ b/common/rfb/scale_sse2.cxx @@ -69,7 +69,7 @@ void SSE2_halve(const uint8_t *oldpx, uint8_t * const dst = newpx + newstride * (y / 2) * 4; - for (x = 0; x < srcw; x += 4) { + for (x = 0; x < srcw - 3; x += 4) { __m128i lo, hi, a, b, c, d; lo = _mm_loadu_si128((__m128i *) &row0[x * 4]); hi = _mm_loadu_si128((__m128i *) &row1[x * 4]); @@ -141,7 +141,7 @@ void SSE2_scale(const uint8_t *oldpx, const __m128i vertmul = _mm_set1_epi16(top); const __m128i vertmul2 = _mm_set1_epi16(bot); - for (x = 0; x < tgtw; x += 2) { + for (x = 0; x < tgtw - 1; x += 2) { const float nx[2] = { x * invdiff, (x + 1) * invdiff, diff --git a/kasmweb b/kasmweb index a4cf389..0bd3813 160000 --- a/kasmweb +++ b/kasmweb @@ -1 +1 @@ -Subproject commit a4cf38902911e9ff07e9d3eb7909c5ba932a5089 +Subproject commit 0bd38139494ef1bbaf580c2f4892c7be3a291b19 diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index a892128..26a9bfd 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -176,15 +176,6 @@ XserverDesktop::queryConnection(network::Socket* sock, return rfb::VNCServerST::PENDING; } -void XserverDesktop::requestClipboard() -{ - try { - server->requestClipboard(); - } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::requestClipboard: %s",e.str()); - } -} - void XserverDesktop::announceClipboard(bool available) { try { @@ -194,12 +185,34 @@ void XserverDesktop::announceClipboard(bool available) } } -void XserverDesktop::sendClipboardData(const char* data) +void XserverDesktop::clearBinaryClipboardData() { try { - server->sendClipboardData(data); + server->clearBinaryClipboardData(); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::sendClipboardData: %s",e.str()); + vlog.error("XserverDesktop::clearBinaryClipboardData: %s",e.str()); + } +} + +void XserverDesktop::sendBinaryClipboardData(const char* mime, + const unsigned char *data, + const unsigned len) +{ + try { + server->sendBinaryClipboardData(mime, data, len); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::sendBinaryClipboardData: %s",e.str()); + } +} + +void XserverDesktop::getBinaryClipboardData(const char* mime, + const unsigned char **data, + unsigned *len) +{ + try { + server->getBinaryClipboardData(mime, data, len); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::getBinaryClipboardData: %s",e.str()); } } @@ -472,19 +485,14 @@ unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, return ret; } -void XserverDesktop::handleClipboardRequest() -{ - vncHandleClipboardRequest(); -} - void XserverDesktop::handleClipboardAnnounce(bool available) { vncHandleClipboardAnnounce(available); } -void XserverDesktop::handleClipboardData(const char* data_, int len) +void XserverDesktop::handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { - vncHandleClipboardData(data_, len); + vncHandleClipboardAnnounceBinary(num, mimes); } void XserverDesktop::grabRegion(const rfb::Region& region) diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 70394fa..fb9b365 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -62,7 +62,11 @@ public: void refreshScreenLayout(); 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); @@ -94,9 +98,8 @@ public: virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual unsigned int setScreenLayout(int fb_width, int fb_height, const rfb::ScreenSet& layout); - virtual void handleClipboardRequest(); virtual void handleClipboardAnnounce(bool available); - virtual void handleClipboardData(const char* data, int len); + virtual void handleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); // rfb::PixelBuffer callbacks virtual void grabRegion(const rfb::Region& r); diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index f3a6ac6..a031b3e 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -295,17 +295,22 @@ Allow click releases inside the blacked-out region. . .TP .B \-DLP_ClipSendMax \fIbytes\fP -Limit clipboard bytes to send to clients in one transaction. Default 10,000. +Limit clipboard bytes to send to clients in one transaction. Default 0. 0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely. . .TP .B \-DLP_ClipAcceptMax \fIbytes\fP -Limit clipboard bytes to receive from clients in one transaction. Default 10,000. +Limit clipboard bytes to receive from clients in one transaction. Default 0. 0 disables the limit, use \fBAcceptCutText\fP to disable clipboard receiving entirely. . .TP .B \-DLP_ClipDelay \fIms\fP -This many milliseconds must pass between clipboard actions. Default 1000. +This many milliseconds must pass between clipboard actions. Default 0, 0 disables the limit. +. +.TP +.B \-DLP_ClipTypes \fIa,b\fP +Allowed binary clipboard mimetypes, separated by commas. Default +chromium/x-web-custom-data,text/html,image/png . .TP .B \-DLP_KeyRateLimit \fIkeys-per-second\fP diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 6b60c0a..66252b9 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,38 @@ static void parseOverrideList(const char *text, ParamSet &out) } } +static void parseClipTypes() +{ + char *str = strdup(Server::DLP_Clip_Types); + char * const origstr = str; + + while (1) { + char *cur = strsep(&str, ","); + if (!cur) + break; + if (!cur[0]) + continue; + + // Hardcoded filters + if (!strcmp(cur, "TEXT") || + !strcmp(cur, "STRING") || + strstr(cur, "text/plain") || + !strcmp(cur, "UTF8_STRING")) + continue; + + struct dlp_mimetype_t m; + strncpy(m.mime, cur, sizeof(m.mime)); + m.mime[sizeof(m.mime) - 1] = '\0'; + + dlp_mimetypes.push_back(m); + + vlog.debug("Adding DLP binary mime type %s", m.mime); + } + vlog.debug("Total %u binary mime types", dlp_mimetypes.size()); + + free(origstr); +} + void vncExtensionInit(void) { if (vncExtGeneration == vncGetServerGeneration()) { @@ -163,6 +199,8 @@ void vncExtensionInit(void) vncAddExtension(); + if (!initialised) + parseClipTypes(); vncSelectionInit(); vlog.info("VNC extension running!"); @@ -315,22 +353,30 @@ void vncUpdateDesktopName(void) desktop[scr]->setDesktopName(desktopName); } -void vncRequestClipboard(void) -{ - for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->requestClipboard(); -} - void vncAnnounceClipboard(int available) { for (int scr = 0; scr < vncGetScreenCount(); scr++) desktop[scr]->announceClipboard(available); } -void vncSendClipboardData(const char* data) +void vncSendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->sendClipboardData(data); + desktop[scr]->sendBinaryClipboardData(mime, data, len); +} + +void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len) +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->getBinaryClipboardData(mime, ptr, len); +} + +void vncClearBinaryClipboardData() +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->clearBinaryClipboardData(); } int vncConnectClient(const char *addr) @@ -486,3 +532,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..0b4abfd 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); @@ -60,9 +67,12 @@ int vncGetSendPrimary(void); 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..bdadd70 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -43,6 +43,14 @@ static Atom xaPRIMARY, xaCLIPBOARD; static Atom xaTARGETS, xaTIMESTAMP, xaSTRING, xaTEXT, xaUTF8_STRING; +static Atom xaINCR; +static Atom *xaBinclips; +static unsigned xaHtmlIndex, xaPngIndex; +static Bool htmlPngPresent; + +static unsigned *mimeIndexesFromClient; +static unsigned numMimesFromClient; +static Bool textFromClient; static WindowPtr pWindow; static Window wid; @@ -66,8 +74,7 @@ static int vncCreateSelectionWindow(void); static int vncOwnSelection(Atom selection); static int vncConvertSelection(ClientPtr client, Atom selection, Atom target, Atom property, - Window requestor, CARD32 time, - const char* data, int len); + Window requestor, CARD32 time); static int vncProcConvertSelection(ClientPtr client); static void vncSelectionRequest(Atom selection, Atom target); static int vncProcSendEvent(ClientPtr client); @@ -90,6 +97,32 @@ void vncSelectionInit(void) xaTEXT = MakeAtom("TEXT", 4, TRUE); xaUTF8_STRING = MakeAtom("UTF8_STRING", 11, TRUE); + xaINCR = MakeAtom("INCR", 4, TRUE); + + unsigned i; + mimeIndexesFromClient = calloc(dlp_num_mimetypes(), sizeof(unsigned)); + numMimesFromClient = 0; + textFromClient = FALSE; + xaBinclips = calloc(dlp_num_mimetypes(), sizeof(Atom)); + htmlPngPresent = FALSE; + Bool htmlfound = FALSE, pngfound = FALSE; + for (i = 0; i < dlp_num_mimetypes(); i++) { + const char *cur = dlp_get_mimetype(i); + xaBinclips[i] = MakeAtom(cur, strlen(cur), TRUE); + + if (!strcmp(cur, "text/html")) { + xaHtmlIndex = i; + htmlfound = TRUE; + } + if (!strcmp(cur, "image/png")) { + xaPngIndex = i; + pngfound = TRUE; + } + } + + if (htmlfound && pngfound) + htmlPngPresent = TRUE; + /* There are no hooks for when these are internal windows, so * override the relevant handlers. */ origProcConvertSelection = ProcVector[X_ConvertSelection]; @@ -103,7 +136,7 @@ void vncSelectionInit(void) FatalError("Add VNC ClientStateCallback failed\n"); } -void vncHandleClipboardRequest(void) +static void vncHandleClipboardRequest(void) { if (activeSelection == None) { LOG_DEBUG("Got request for local clipboard although no clipboard is active"); @@ -161,24 +194,55 @@ void vncHandleClipboardAnnounce(int available) } } -void vncHandleClipboardData(const char* data, int len) +void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]) { - struct VncDataTarget* next; - - LOG_DEBUG("Got remote clipboard data, sending to X11 clients"); - - while (vncDataTargetHead != NULL) { + if (num) { int rc; - xEvent event; - rc = vncConvertSelection(vncDataTargetHead->client, - vncDataTargetHead->selection, - vncDataTargetHead->target, - vncDataTargetHead->property, - vncDataTargetHead->requestor, - vncDataTargetHead->time, - data, len); - if (rc != Success) { + LOG_DEBUG("Remote binary clipboard announced, grabbing local ownership"); + + if (vncGetSetPrimary()) { + rc = vncOwnSelection(xaPRIMARY); + if (rc != Success) + LOG_ERROR("Could not set PRIMARY selection"); + } + + rc = vncOwnSelection(xaCLIPBOARD); + if (rc != Success) + LOG_ERROR("Could not set CLIPBOARD selection"); + + unsigned i, valid = 0; + for (i = 0; i < num; i++) { + unsigned j; + for (j = 0; j < dlp_num_mimetypes(); j++) { + if (!strcmp(dlp_get_mimetype(j), mimes[i])) { + mimeIndexesFromClient[valid] = j; + valid++; + break; + } + } + + if (!strcmp(mimes[i], "text/plain")) + textFromClient = TRUE; + } + numMimesFromClient = valid; + LOG_DEBUG("Client sent %u mimes, %u were valid", num, valid); + } else { + struct VncDataTarget* next; + numMimesFromClient = 0; + textFromClient = FALSE; + + if (pWindow == NULL) + return; + + LOG_DEBUG("Remote binary clipboard lost, removing local ownership"); + + DeleteWindowFromAnySelections(pWindow); + + /* Abort any pending transfer */ + while (vncDataTargetHead != NULL) { + xEvent event; + event.u.u.type = SelectionNotify; event.u.selectionNotify.time = vncDataTargetHead->time; event.u.selectionNotify.requestor = vncDataTargetHead->requestor; @@ -186,11 +250,11 @@ void vncHandleClipboardData(const char* data, int len) event.u.selectionNotify.target = vncDataTargetHead->target; event.u.selectionNotify.property = None; WriteEventsToClient(vncDataTargetHead->client, 1, &event); - } - next = vncDataTargetHead->next; - free(vncDataTargetHead); - vncDataTargetHead = next; + next = vncDataTargetHead->next; + free(vncDataTargetHead); + vncDataTargetHead = next; + } } } @@ -277,10 +341,22 @@ 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, - const char* data, int len) + Window requestor, CARD32 time) { Selection *pSel; WindowPtr pWin; @@ -290,13 +366,8 @@ static int vncConvertSelection(ClientPtr client, Atom selection, xEvent event; - if (data == NULL) { - LOG_DEBUG("Selection request for %s (type %s)", - NameForAtom(selection), NameForAtom(target)); - } else { - LOG_DEBUG("Sending data for selection request for %s (type %s)", - NameForAtom(selection), NameForAtom(target)); - } + LOG_DEBUG("Selection request for %s (type %s)", + NameForAtom(selection), NameForAtom(target)); rc = dixLookupSelection(&pSel, selection, client, DixGetAttrAccess); if (rc != Success) @@ -315,14 +386,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,59 +413,47 @@ static int vncConvertSelection(ClientPtr client, Atom selection, TRUE); if (rc != Success) return rc; - } else { - if (data == NULL) { - struct VncDataTarget* vdt; + } 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 (textFromClient) { + const unsigned char *data; + unsigned len; + vncGetBinaryClipboardData("text/plain", &data, &len); - if ((target != xaSTRING) && (target != xaTEXT) && - (target != xaUTF8_STRING)) - return BadMatch; - - vdt = calloc(1, sizeof(struct VncDataTarget)); - if (vdt == NULL) + if ((target == xaSTRING) || (target == xaTEXT)) { + char* latin1; + latin1 = vncUTF8ToLatin1(data, (size_t)-1); + if (latin1 == NULL) return BadAlloc; - vdt->client = client; - vdt->selection = selection; - vdt->target = target; - vdt->property = property; - vdt->requestor = requestor; - vdt->time = time; + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + XA_STRING, 8, PropModeReplace, + len, latin1, TRUE); - vdt->next = vncDataTargetHead; - vncDataTargetHead = vdt; + vncStrFree(latin1); - LOG_DEBUG("Requesting clipboard data from client"); - - vncRequestClipboard(); - - return Success; + if (rc != Success) + return rc; + } else if (target == xaUTF8_STRING) { + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + xaUTF8_STRING, 8, PropModeReplace, + len, (char *) data, TRUE); + if (rc != Success) + return rc; } else { - if ((target == xaSTRING) || (target == xaTEXT)) { - char* latin1; - - latin1 = vncUTF8ToLatin1(data, (size_t)-1); - if (latin1 == NULL) - return BadAlloc; - - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - XA_STRING, 8, PropModeReplace, - len, latin1, TRUE); - - vncStrFree(latin1); - - if (rc != Success) - return rc; - } else if (target == xaUTF8_STRING) { - rc = dixChangeWindowProperty(serverClient, pWin, realProperty, - xaUTF8_STRING, 8, PropModeReplace, - len, data, TRUE); - if (rc != Success) - return rc; - } else { - return BadMatch; - } + return BadMatch; } + } else { + LOG_ERROR("Text clipboard paste requested, but client sent no text"); + + return BadMatch; } event.u.u.type = SelectionNotify; @@ -424,7 +492,7 @@ static int vncProcConvertSelection(ClientPtr client) pSel->window == wid) { rc = vncConvertSelection(client, stuff->selection, stuff->target, stuff->property, - stuff->requestor, stuff->time, NULL, 0); + stuff->requestor, stuff->time); if (rc != Success) { xEvent event; @@ -483,6 +551,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) @@ -502,6 +585,9 @@ static void vncHandleSelection(Atom selection, Atom target, if (target != property) return; + if (prop->type == xaINCR) + LOG_INFO("Incremental clipboard transfer denied, too large"); + if (target == xaTARGETS) { if (prop->format != 32) return; @@ -510,16 +596,36 @@ 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"); + vncClearBinaryClipboardData(); activeSelection = selection; vncAnnounceClipboard(TRUE); + vncHandleClipboardRequest(); } } else { if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaUTF8_STRING); else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaSTRING); + + unsigned i; + + Bool skiphtml = FALSE; + if (htmlPngPresent && + vncHasAtom(xaBinclips[xaHtmlIndex], (const Atom*)prop->data, prop->size) && + vncHasAtom(xaBinclips[xaPngIndex], (const Atom*)prop->data, prop->size)) + skiphtml = TRUE; + + for (i = 0; i < dlp_num_mimetypes(); i++) { + if (skiphtml && i == xaHtmlIndex) + continue; + if (vncHasAtom(xaBinclips[i], (const Atom*)prop->data, prop->size)) { + vncSelectionRequest(selection, xaBinclips[i]); + //break; + } + } } } else if (target == xaSTRING) { char* filtered; @@ -539,10 +645,10 @@ static void vncHandleSelection(Atom selection, Atom target, if (utf8 == NULL) return; - LOG_DEBUG("Sending clipboard to clients (%d bytes)", + LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)", (int)strlen(utf8)); - vncSendClipboardData(utf8); + vncSendBinaryClipboardData("text/plain", utf8, strlen(utf8)); vncStrFree(utf8); } else if (target == xaUTF8_STRING) { @@ -557,12 +663,31 @@ static void vncHandleSelection(Atom selection, Atom target, if (filtered == NULL) return; - LOG_DEBUG("Sending clipboard to clients (%d bytes)", + LOG_DEBUG("Sending text part of binary clipboard to clients (%d bytes)", (int)strlen(filtered)); - vncSendClipboardData(filtered); + vncSendBinaryClipboardData("text/plain", filtered, strlen(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..68266bf 100644 --- a/unix/xserver/hw/vnc/vncSelection.h +++ b/unix/xserver/hw/vnc/vncSelection.h @@ -24,9 +24,8 @@ extern "C" { void vncSelectionInit(void); -void vncHandleClipboardRequest(void); void vncHandleClipboardAnnounce(int available); -void vncHandleClipboardData(const char* data, int len); +void vncHandleClipboardAnnounceBinary(const unsigned num, const char mimes[][32]); #ifdef __cplusplus }