Support extended clipboard transfers
Implements support in both client and server for the extended
clipboard format first seen in UltraVNC. Currently only implements
text handling, but that is still an improvement as it extends the
clipboard from ISO 8859-1 to full Unicode.
diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx
index 86288ad..a9e12d7 100644
--- a/common/rfb/CMsgReader.cxx
+++ b/common/rfb/CMsgReader.cxx
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2017 Pierre Ossman for Cendio AB
+ * Copyright 2009-2019 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,8 +20,11 @@
#include <assert.h>
#include <stdio.h>
-#include <rfb/msgTypes.h>
#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+
+#include <rfb/msgTypes.h>
+#include <rfb/clipboardTypes.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/util.h>
@@ -30,6 +33,8 @@
static rfb::LogWriter vlog("CMsgReader");
+static rfb::IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
+
using namespace rfb;
CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
@@ -152,7 +157,15 @@
{
is->skip(3);
rdr::U32 len = is->readU32();
- if (len > 256*1024) {
+
+ if (len & 0x80000000) {
+ rdr::S32 slen = len;
+ slen = -slen;
+ readExtendedClipboard(slen);
+ return;
+ }
+
+ if (len > (size_t)maxCutText) {
is->skip(len);
vlog.error("cut text too long (%d bytes) - ignoring",len);
return;
@@ -163,6 +176,99 @@
handler->serverCutText(filtered.buf);
}
+void CMsgReader::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.removeUnderlying();
+
+ 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");
+ }
+ }
+}
+
void CMsgReader::readFence()
{
rdr::U32 flags;