diff --git a/README.md b/README.md index a618547..65d84f4 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ Future Goals: #### Debian-based ```sh -wget -qO- https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb +wget https://github.com/kasmtech/KasmVNC/releases/download/v0.9.2-beta/kasmvncserver_ubuntu_bionic_0.9.2_amd64.deb -sudo dpkg -i kasmvncserver_0.9.1~beta-1_amd64.deb +sudo dpkg -i kasmvncserver_*.deb sudo apt-get -f install # We provide an example script to run KasmVNC at # diff --git a/common/network/websocket.c b/common/network/websocket.c index 47ac11d..6270b49 100644 --- a/common/network/websocket.c +++ b/common/network/websocket.c @@ -111,7 +111,7 @@ static const char *parse_get(const char * const in, const char * const opt, unsi return ""; } -static void percent_decode(const char *src, char *dst) { +static void percent_decode(const char *src, char *dst, const uint8_t filter) { while (1) { if (!*src) break; @@ -127,7 +127,32 @@ static void percent_decode(const char *src, char *dst) { hex[2] = '\0'; src += 2; - *dst++ = strtol(hex, NULL, 16); + *dst = strtol(hex, NULL, 16); + + if (filter) { + // Avoid directory traversal + if (*dst == '/') + *dst = '_'; + } + + dst++; + } + } + + *dst = '\0'; +} + +static void percent_encode(const char *src, char *dst) { + while (1) { + if (!*src) + break; + if (isalnum(*src) || *src == '.' || *src == ',') { + *dst++ = *src++; + } else { + *dst++ = '%'; + sprintf(dst, "%02X", *src); + dst += 2; + src++; } } @@ -746,6 +771,7 @@ def: static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) { char buf[4096]; + char enc[PATH_MAX * 3 + 1]; unsigned i; // Redirect? @@ -779,11 +805,13 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[ if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, "..")) continue; + percent_encode(names[i]->d_name, enc); + if (names[i]->d_type == DT_DIR) - sprintf(buf, "
  • %s/
  • ", names[i]->d_name, + sprintf(buf, "
  • %s/
  • ", enc, names[i]->d_name); else - sprintf(buf, "
  • %s
  • ", names[i]->d_name, + sprintf(buf, "
  • %s
  • ", enc, names[i]->d_name); ws_send(ws_ctx, buf, strlen(buf)); @@ -822,13 +850,15 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) { len = strlen(path); } - wserr("Requested file '%s'\n", path); - sprintf(fullpath, "%s/%s", settings.httpdir, path); + percent_decode(path, buf, 1); + + wserr("Requested file '%s'\n", buf); + sprintf(fullpath, "%s/%s", settings.httpdir, buf); DIR *dir = opendir(fullpath); if (dir) { closedir(dir); - dirlisting(ws_ctx, fullpath, path); + dirlisting(ws_ctx, fullpath, buf); return; } @@ -976,14 +1006,14 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } param = parse_get(args, "password", &len); if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decpw); + percent_decode(buf, decpw, 0); struct crypt_data cdata; cdata.initialized = 0; @@ -1023,7 +1053,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1052,7 +1082,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } if (!decname[0]) @@ -1097,7 +1127,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) { if (len) { memcpy(buf, param, len); buf[len] = '\0'; - percent_decode(buf, decname); + percent_decode(buf, decname, 0); } else { wserr("client param required\n"); goto nope; @@ -1320,6 +1350,8 @@ ws_ctx_t *do_handshake(int sock) { } else { // Client tried an empty password, just fail them response[0] = '\0'; + authbuf[0] = 'a'; + authbuf[1] = '\0'; } } diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h index 806625f..e16a810 100644 --- a/common/rfb/InputHandler.h +++ b/common/rfb/InputHandler.h @@ -38,7 +38,9 @@ namespace rfb { virtual void pointerEvent(const Point& __unused_attr pos, int __unused_attr buttonMask, const bool __unused_attr skipClick, - const bool __unused_attr skipRelease) { } + const bool __unused_attr skipRelease, + int scrollX, + int scrollY) { } virtual void clientCutText(const char* __unused_attr str, int __unused_attr len) { } }; 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); -} - -void SConnection::handleClipboardRequest(rdr::U32 flags) -{ - if (!(flags & rfb::clipboardUTF8)) - return; - if (!hasLocalClipboard) - return; - handleClipboardRequest(); + binaryClipboard.clear(); } -void SConnection::handleClipboardPeek(rdr::U32 flags) +void SConnection::addBinaryClipboard(const char mime[], const rdr::U8 *data, + const rdr::U32 len) { - if (!hasLocalClipboard) - return; - if (cp.clipboardFlags() & rfb::clipboardNotify) - writer()->writeClipboardNotify(rfb::clipboardUTF8); -} + binaryClipboard_t bin; + strncpy(bin.mime, mime, sizeof(bin.mime)); + bin.mime[sizeof(bin.mime) - 1] = '\0'; -void SConnection::handleClipboardNotify(rdr::U32 flags) -{ - strFree(clientClipboard); - clientClipboard = NULL; + bin.data.resize(len); + memcpy(&bin.data[0], data, len); - if (flags & rfb::clipboardUTF8) { - handleClipboardAnnounce(true); - hasLocalClipboard = false; - } else { - handleClipboardAnnounce(false); - } + binaryClipboard.push_back(bin); } -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) +void SMsgHandler::handleClipboardAnnounceBinary(const unsigned, const char mimes[][32]) { - cp.setClipboardCaps(flags, lengths); } -void SMsgHandler::handleClipboardRequest(rdr::U32 flags) +void SMsgHandler::clearBinaryClipboard() { } -void SMsgHandler::handleClipboardPeek(rdr::U32 flags) -{ -} - -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 de5e1b3..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; @@ -223,7 +224,9 @@ void SMsgReader::readPointerEvent() int mask = is->readU8(); int x = is->readU16(); int y = is->readU16(); - handler->pointerEvent(Point(x, y), mask, false, false); + int scrollX = is->readS16(); + int scrollY = is->readS16(); + handler->pointerEvent(Point(x, y), mask, false, false, scrollX, scrollY); } @@ -238,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; - } - CharArray ca(len+1); - ca.buf[len] = 0; - is->readBytes(ca.buf, len); - handler->clientCutText(ca.buf, len); + is->skip(len); + vlog.error("Client sent old cuttext msg, ignoring"); } -void SMsgReader::readExtendedClipboard(rdr::S32 len) +void SMsgReader::readBinaryClipboard() { - 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; + 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); + } - if (action & clipboardCaps) { - int i; - size_t num; - rdr::U32 lengths[16]; + char mime[mimelen + 1]; + mime[mimelen] = '\0'; + is->readBytes(mime, mimelen); - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - num++; - } + strncpy(tmpmimes[valid], mime, 32); + tmpmimes[valid][31] = '\0'; - if (len < (rdr::S32)(4 + 4*num)) - throw Exception("Invalid extended clipboard message"); + const rdr::U32 len = is->readU32(); + CharArray ca(len); + is->readBytes(ca.buf, len); - num = 0; - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) - lengths[num++] = is->readU32(); + if (rfb::Server::DLP_ClipAcceptMax && len > (unsigned) rfb::Server::DLP_ClipAcceptMax) { + vlog.info("DLP: refused to receive binary clipboard, too large"); + continue; } - 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++; - } + vlog.debug("Received binary clipboard, type %s, %u bytes", mime, len); - zis.flushUnderlying(); - zis.setUnderlying(NULL, 0); + handler->addBinaryClipboard(mime, (rdr::U8 *) ca.buf, len); + valid++; + } - handler->handleClipboardProvide(flags, lengths, buffers); + handler->handleClipboardAnnounceBinary(valid, tmpmimes); +} - 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"); - } - } +void SMsgReader::readExtendedClipboard(rdr::S32 len) +{ + if (len < 4) + throw Exception("Invalid extended clipboard message"); + 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(); -} - -void SMsgWriter::writeClipboardCaps(rdr::U32 caps, - const rdr::U32* lengths) -{ - size_t i, count; - - if (!cp->supportsExtendedClipboard) - throw Exception("Client does not support extended clipboard"); - - count = 0; - for (i = 0;i < 16;i++) { - if (caps & (1 << i)) - count++; - } - - 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(); -} + startMsg(msgTypeBinaryClipboard); -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"); + 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); - 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++; + os->writeU32(b[i].data.size()); + os->writeBytes(&b[i].data[0], b[i].data.size()); } - 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 bd24229..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 { @@ -692,7 +703,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) setCursor(); } -void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease) +void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY) { pointerEventTime = lastEventTime = time(0); server->lastUserInputTime = lastEventTime; @@ -720,7 +731,7 @@ void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool } } - server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease); + server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease, scrollX, scrollY); } } @@ -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 86c99c6..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 @@ -204,7 +207,7 @@ namespace rfb { virtual void queryConnection(const char* userName); virtual void clientInit(bool shared); virtual void setPixelFormat(const PixelFormat& pf); - virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease); + virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease, int scrollX, int scrollY); virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); virtual void framebufferUpdateRequest(const Rect& r, bool incremental); virtual void setDesktopSize(int fb_width, int fb_height, @@ -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; + for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { + ci_next = ci; ci_next++; + (*ci)->sendBinaryClipboardDataOrClose(mime, data, len); + } +} - if (strchr(data, '\r') != NULL) - throw Exception("Invalid carriage return in clipboard data"); +void VNCServerST::getBinaryClipboardData(const char* mime, const unsigned char **data, + unsigned *len) +{ + if (!clipboardClient) + return; + clipboardClient->getBinaryClipboardData(mime, data, len); +} - for (ci = clipboardRequestors.begin(); - ci != clipboardRequestors.end(); ci = ci_next) { +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)->sendClipboardDataOrClose(data); + (*ci)->clearBinaryClipboardData(); } - - clipboardRequestors.clear(); } 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/unix/xserver/hw/vnc/Input.c b/unix/xserver/hw/vnc/Input.c index f8ac3fd..e8f9170 100644 --- a/unix/xserver/hw/vnc/Input.c +++ b/unix/xserver/hw/vnc/Input.c @@ -234,6 +234,14 @@ void vncPointerMove(int x, int y) cursorPosY = y; } +void vncScroll(int x, int y) { + ValuatorMask mask; + valuator_mask_zero(&mask); + valuator_mask_set(&mask, 2, x); + valuator_mask_set(&mask, 3, y); + QueuePointerEvents(vncPointerDev, MotionNotify, 0, POINTER_RELATIVE, &mask); +} + void vncGetPointerPos(int *x, int *y) { if (vncPointerDev != NULL) { @@ -261,7 +269,7 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff) * is not a bug. */ Atom btn_labels[BUTTONS]; - Atom axes_labels[2]; + Atom axes_labels[4]; switch (onoff) { case DEVICE_INIT: @@ -278,11 +286,29 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff) axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); + axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL); + axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL); + InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels, (PtrCtrlProcPtr)NoopDDA, GetMotionHistorySize(), - 2, axes_labels); + 4, axes_labels); + + InitValuatorAxisStruct(pDevice, 2, axes_labels[2], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative); + InitValuatorAxisStruct(pDevice, 3, axes_labels[3], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative); + + char* envScrollFactorH = getenv("SCROLL_FACTOR_H"); + char* envScrollFactorV = getenv("SCROLL_FACTOR_V"); + + float scrollFactorH = envScrollFactorH ? atof(envScrollFactorH) : 50.0; + float scrollFactorV = envScrollFactorV ? atof(envScrollFactorV) : 50.0; + + LOG_INFO("Mouse horizonatl scroll factor: %f", scrollFactorH); + LOG_INFO("Mouse vertical scroll factor: %f", scrollFactorV); + + SetScrollValuator(pDevice, 2, SCROLL_TYPE_HORIZONTAL, scrollFactorH, SCROLL_FLAG_NONE); + SetScrollValuator(pDevice, 3, SCROLL_TYPE_VERTICAL, scrollFactorV, SCROLL_FLAG_PREFERRED); break; case DEVICE_ON: pDev->on = TRUE; diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h index 76680c0..ddc4e06 100644 --- a/unix/xserver/hw/vnc/Input.h +++ b/unix/xserver/hw/vnc/Input.h @@ -35,6 +35,7 @@ void vncInitInputDevice(void); void vncPointerButtonAction(int buttonMask, const unsigned char skipclick, const unsigned char skiprelease); void vncPointerMove(int x, int y); +void vncScroll(int x, int y); void vncGetPointerPos(int *x, int *y); void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down); diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 1e9d663..26a9bfd 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -176,30 +176,43 @@ XserverDesktop::queryConnection(network::Socket* sock, return rfb::VNCServerST::PENDING; } -void XserverDesktop::requestClipboard() +void XserverDesktop::announceClipboard(bool available) { try { - server->requestClipboard(); + server->announceClipboard(available); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::requestClipboard: %s",e.str()); + vlog.error("XserverDesktop::announceClipboard: %s",e.str()); } } -void XserverDesktop::announceClipboard(bool available) +void XserverDesktop::clearBinaryClipboardData() { try { - server->announceClipboard(available); + server->clearBinaryClipboardData(); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::announceClipboard: %s",e.str()); + vlog.error("XserverDesktop::clearBinaryClipboardData: %s",e.str()); } } -void XserverDesktop::sendClipboardData(const char* data) +void XserverDesktop::sendBinaryClipboardData(const char* mime, + const unsigned char *data, + const unsigned len) { try { - server->sendClipboardData(data); + server->sendBinaryClipboardData(mime, data, len); } catch (rdr::Exception& e) { - vlog.error("XserverDesktop::sendClipboardData: %s",e.str()); + 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()); } } @@ -446,11 +459,14 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept, void XserverDesktop::pointerEvent(const Point& pos, int buttonMask, - const bool skipClick, const bool skipRelease) + const bool skipClick, const bool skipRelease, int scrollX, int scrollY) { - vncPointerMove(pos.x + vncGetScreenX(screenIndex), - pos.y + vncGetScreenY(screenIndex)); - vncPointerButtonAction(buttonMask, skipClick, skipRelease); + if (scrollX == 0 && scrollY == 0) { + vncPointerMove(pos.x + vncGetScreenX(screenIndex), pos.y + vncGetScreenY(screenIndex)); + vncPointerButtonAction(buttonMask, skipClick, skipRelease); + } else { + vncScroll(scrollX, scrollY); + } } unsigned int XserverDesktop::setScreenLayout(int fb_width, int fb_height, @@ -469,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 014a48e..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); @@ -90,13 +94,12 @@ public: // rfb::SDesktop callbacks virtual void pointerEvent(const rfb::Point& pos, int buttonMask, - const bool skipClick, const bool skipRelease); + const bool skipClick, const bool skipRelease, int scrollX = 0, int scrollY = 0); 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) +void vncAnnounceClipboard(int available) +{ + for (int scr = 0; scr < vncGetScreenCount(); scr++) + desktop[scr]->announceClipboard(available); +} + +void vncSendBinaryClipboardData(const char* mime, const unsigned char *data, + const unsigned len) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->requestClipboard(); + desktop[scr]->sendBinaryClipboardData(mime, data, len); } -void vncAnnounceClipboard(int available) +void vncGetBinaryClipboardData(const char *mime, const unsigned char **ptr, + unsigned *len) { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->announceClipboard(available); + desktop[scr]->getBinaryClipboardData(mime, ptr, len); } -void vncSendClipboardData(const char* data) +void vncClearBinaryClipboardData() { for (int scr = 0; scr < vncGetScreenCount(); scr++) - desktop[scr]->sendClipboardData(data); + 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; + if (num) { + int rc; - LOG_DEBUG("Got remote clipboard data, sending to X11 clients"); + 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; - while (vncDataTargetHead != NULL) { - int rc; - xEvent event; - - rc = vncConvertSelection(vncDataTargetHead->client, - vncDataTargetHead->selection, - vncDataTargetHead->target, - vncDataTargetHead->property, - vncDataTargetHead->requestor, - vncDataTargetHead->time, - data, len); - if (rc != Success) { 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; - - if ((target != xaSTRING) && (target != xaTEXT) && - (target != xaUTF8_STRING)) - return BadMatch; - - vdt = calloc(1, sizeof(struct VncDataTarget)); - if (vdt == NULL) + } 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)) { + 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; - - vdt->next = vncDataTargetHead; - vncDataTargetHead = vdt; + rc = dixChangeWindowProperty(serverClient, pWin, realProperty, + XA_STRING, 8, PropModeReplace, + len, latin1, TRUE); - LOG_DEBUG("Requesting clipboard data from client"); + vncStrFree(latin1); - 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 }