Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/neutrinolabs/xrdp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/vnc
diff options
context:
space:
mode:
authormatt335672 <30179339+matt335672@users.noreply.github.com>2021-05-27 17:35:52 +0300
committermatt335672 <30179339+matt335672@users.noreply.github.com>2021-06-08 16:11:17 +0300
commitfe28af93e888e2617d310f4bded7ae468095124c (patch)
tree0d891fabc4a8af381da53ded11f0f660ccbf39f2 /vnc
parent3ea19ef0cd41a91165e03713cb6a8328ffa1d492 (diff)
Split up vnc.c into modules and re-implement clipboard functions
Diffstat (limited to 'vnc')
-rw-r--r--vnc/Makefile.am5
-rw-r--r--vnc/rfb.c53
-rw-r--r--vnc/rfb.h67
-rw-r--r--vnc/vnc.c401
-rw-r--r--vnc/vnc.h18
-rw-r--r--vnc/vnc_clip.c1031
-rw-r--r--vnc/vnc_clip.h70
7 files changed, 1309 insertions, 336 deletions
diff --git a/vnc/Makefile.am b/vnc/Makefile.am
index ebe02c05..25e7588c 100644
--- a/vnc/Makefile.am
+++ b/vnc/Makefile.am
@@ -10,7 +10,10 @@ module_LTLIBRARIES = \
libvnc_la_SOURCES = \
vnc.c \
- vnc.h
+ vnc_clip.c \
+ rfb.h \
+ vnc.h \
+ vnc_clip.h
libvnc_la_LIBADD = \
$(top_builddir)/common/libcommon.la
diff --git a/vnc/rfb.c b/vnc/rfb.c
new file mode 100644
index 00000000..0f8f13b2
--- /dev/null
+++ b/vnc/rfb.c
@@ -0,0 +1,53 @@
+/**
+ * xrdp: A Remote Desktop Protocol server.
+ *
+ * Copyright (C) Jay Sorg 2004-2015
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * libvnc - RFB specific functions
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include <config_ac.h>
+#endif
+
+#include "rfb.h"
+
+/* Messages for ExtendedDesktopSize status code */
+static const char *eds_status_msg[] =
+{
+ /* 0 */ "No error",
+ /* 1 */ "Resize is administratively prohibited",
+ /* 2 */ "Out of resources",
+ /* 3 */ "Invalid screen layout",
+ /* others */ "Unknown code"
+};
+
+/* elements in above list */
+#define EDS_STATUS_MSG_COUNT \
+ (sizeof(eds_status_msg) / sizeof(eds_status_msg[0]))
+
+/**************************************************************************//**
+ * Returns an error string for an ExtendedDesktopSize status code
+ */
+const char *
+get_eds_status_msg(unsigned int response_code)
+{
+ if (response_code >= EDS_STATUS_MSG_COUNT)
+ {
+ response_code = EDS_STATUS_MSG_COUNT - 1;
+ }
+
+ return eds_status_msg[response_code];
+}
diff --git a/vnc/rfb.h b/vnc/rfb.h
new file mode 100644
index 00000000..4dfbf54f
--- /dev/null
+++ b/vnc/rfb.h
@@ -0,0 +1,67 @@
+/**
+ * xrdp: A Remote Desktop Protocol server.
+ *
+ * Copyright (C) Jay Sorg 2004-2015
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * libvnc - defines related to the RFB protocol
+ */
+
+#ifndef RFB_H
+#define RFB_H
+
+#include "arch.h"
+
+/* Client-to-server messages */
+enum c2s
+{
+ RFB_C2S_SET_PIXEL_FORMAT = 0,
+ RFB_C2S_SET_ENCODINGS = 2,
+ RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST = 3,
+ RFB_C2S_KEY_EVENT = 4,
+ RFB_C2S_POINTER_EVENT = 5,
+ RFB_C2S_CLIENT_CUT_TEXT = 6,
+};
+
+/* Server to client messages */
+enum s2c
+{
+ RFB_S2C_FRAMEBUFFER_UPDATE = 0,
+ RFB_S2C_SET_COLOUR_MAP_ENTRIES = 1,
+ RFB_S2C_BELL = 2,
+ RFB_S2C_SERVER_CUT_TEXT = 3
+};
+
+/* Encodings and pseudo-encodings
+ *
+ * The RFC uses a signed type for these. We use an unsigned type as the
+ * binary representation for the negative values is standardised in C
+ * (which it wouldn't be for an enum value)
+ */
+
+typedef uint32_t encoding_type;
+
+#define RFB_ENC_RAW (encoding_type)0
+#define RFB_ENC_COPY_RECT (encoding_type)1
+#define RFB_ENC_CURSOR (encoding_type)-239
+#define RFB_ENC_DESKTOP_SIZE (encoding_type)-223
+#define RFB_ENC_EXTENDED_DESKTOP_SIZE (encoding_type)-308
+
+/**
+ * Returns an error string for an ExtendedDesktopSize status code
+ */
+const char *
+rfb_get_eds_status_msg(unsigned int response_code);
+
+#endif /* RFB_H */
diff --git a/vnc/vnc.c b/vnc/vnc.c
index a34f73db..179867a1 100644
--- a/vnc/vnc.c
+++ b/vnc/vnc.c
@@ -31,57 +31,14 @@
#endif
#include "vnc.h"
+#include "vnc_clip.h"
+#include "rfb.h"
#include "log.h"
#include "trans.h"
#include "ssl_calls.h"
#include "string_calls.h"
#include "xrdp_client_info.h"
-/* Client-to-server messages */
-enum c2s
-{
- C2S_SET_PIXEL_FORMAT = 0,
- C2S_SET_ENCODINGS = 2,
- C2S_FRAMEBUFFER_UPDATE_REQUEST = 3,
- C2S_KEY_EVENT = 4,
- C2S_POINTER_EVENT = 5,
- C2S_CLIENT_CUT_TEXT = 6,
-};
-
-/* Server to client messages */
-enum s2c
-{
- S2C_FRAMEBUFFER_UPDATE = 0,
- S2C_SET_COLOUR_MAP_ENTRIES = 1,
- S2C_BELL = 2,
- S2C_SERVER_CUT_TEXT = 3
-};
-
-/* Encodings and pseudo-encodings
- *
- * The RFC uses a signed type for these. We use an unsigned type as the
- * binary representation for the negative values is standardised in C
- * (which it wouldn't be for an enum value)
- */
-
-typedef uint32_t encoding_type;
-
-#define ENC_RAW (encoding_type)0
-#define ENC_COPY_RECT (encoding_type)1
-#define ENC_CURSOR (encoding_type)-239
-#define ENC_DESKTOP_SIZE (encoding_type)-223
-#define ENC_EXTENDED_DESKTOP_SIZE (encoding_type)-308
-
-/* Messages for ExtendedDesktopSize status code */
-static const char *eds_status_msg[] =
-{
- /* 0 */ "No error",
- /* 1 */ "Resize is administratively prohibited",
- /* 2 */ "Out of resources",
- /* 3 */ "Invalid screen layout",
- /* others */ "Unknown code"
-};
-
/* elements in above list */
#define EDS_STATUS_MSG_COUNT \
(sizeof(eds_status_msg) / sizeof(eds_status_msg[0]))
@@ -92,11 +49,8 @@ enum
MSK_EXTENDED_DESKTOP_SIZE = (1 << 0)
};
-static int
-lib_mod_process_message(struct vnc *v, struct stream *s);
-
/******************************************************************************/
-static int
+int
lib_send_copy(struct vnc *v, struct stream *s)
{
return trans_write_copy_s(v->trans, s);
@@ -152,160 +106,6 @@ rfbHashEncryptBytes(char *bytes, const char *passwd)
rfbEncryptBytes(bytes, passwd_hash_text);
}
-/******************************************************************************/
-static int
-lib_process_channel_data(struct vnc *v, int chanid, int flags, int size,
- struct stream *s, int total_size)
-{
- int type;
- int status;
- int length;
- int clip_bytes;
- int index;
- int format;
- struct stream *out_s;
-
- if (chanid == v->clip_chanid)
- {
- in_uint16_le(s, type);
- in_uint16_le(s, status);
- in_uint32_le(s, length);
-
- LOG(LOG_LEVEL_DEBUG, "clip data type %d status %d length %d", type, status, length);
- LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "clipboard data", s->p, s->end - s->p);
- switch (type)
- {
- case 2: /* CLIPRDR_FORMAT_ANNOUNCE */
- LOG(LOG_LEVEL_DEBUG, "CLIPRDR_FORMAT_ANNOUNCE - "
- "status %d length %d", status, length);
- make_stream(out_s);
- init_stream(out_s, 8192);
- out_uint16_le(out_s, 3); // msg-type: CLIPRDR_FORMAT_ACK
- out_uint16_le(out_s, 1); // msg-status-code: CLIPRDR_RESPONSE
- out_uint32_le(out_s, 0); // null (?)
- out_uint8s(out_s, 4); // pad
- s_mark_end(out_s);
- length = (int)(out_s->end - out_s->data);
- v->server_send_to_channel(v, v->clip_chanid, out_s->data,
- length, length, 3);
- free_stream(out_s);
-#if 0
- // Send the CLIPRDR_DATA_REQUEST message to the cliprdr channel.
- //
- make_stream(out_s);
- init_stream(out_s, 8192);
- out_uint16_le(out_s, 4); // msg-type: CLIPRDR_DATA_REQUEST
- out_uint16_le(out_s, 0); // msg-status-code: CLIPRDR_REQUEST
- out_uint32_le(out_s, 4); // sizeof supported-format-list.
- out_uint32_le(out_s, 1); // supported-format-list: CF_TEXT
- out_uint8s(out_s, 0); // pad (garbage pad?)
- s_mark_end(out_s);
- length = (int)(out_s->end - out_s->data);
- v->server_send_to_channel(v, v->clip_chanid, out_s->data,
- length, length, 3);
- free_stream(out_s);
-#endif
- break;
-
- case 3: /* CLIPRDR_FORMAT_ACK */
- LOG(LOG_LEVEL_DEBUG, "CLIPRDR_FORMAT_ACK - "
- "status %d length %d", status, length);
- break;
- case 4: /* CLIPRDR_DATA_REQUEST */
- LOG(LOG_LEVEL_DEBUG, "CLIPRDR_DATA_REQUEST - "
- "status %d length %d", status, length);
- format = 0;
-
- if (length >= 4)
- {
- in_uint32_le(s, format);
- }
-
- /* only support CF_TEXT and CF_UNICODETEXT */
- if ((format != 1) && (format != 13))
- {
- break;
- }
-
- make_stream(out_s);
- init_stream(out_s, 8192);
- out_uint16_le(out_s, 5);
- out_uint16_le(out_s, 1);
-
- if (format == 13) /* CF_UNICODETEXT */
- {
- out_uint32_le(out_s, v->clip_data_s->size * 2 + 2);
-
- for (index = 0; index < v->clip_data_s->size; index++)
- {
- out_uint8(out_s, v->clip_data_s->data[index]);
- out_uint8(out_s, 0);
- }
-
- out_uint8s(out_s, 2);
- }
- else if (format == 1) /* CF_TEXT */
- {
- out_uint32_le(out_s, v->clip_data_s->size + 1);
-
- for (index = 0; index < v->clip_data_s->size; index++)
- {
- out_uint8(out_s, v->clip_data_s->data[index]);
- }
-
- out_uint8s(out_s, 1);
- }
-
- out_uint8s(out_s, 4); /* pad */
- s_mark_end(out_s);
- length = (int)(out_s->end - out_s->data);
- v->server_send_to_channel(v, v->clip_chanid, out_s->data, length,
- length, 3);
- free_stream(out_s);
- break;
-
- case 5: /* CLIPRDR_DATA_RESPONSE */
- LOG(LOG_LEVEL_DEBUG, "CLIPRDR_DATA_RESPONSE - "
- "status %d length %d", status, length);
- clip_bytes = MIN(length, 256);
- // - Read the response data from the cliprdr channel, stream 's'.
- // - Send the response data to the vnc server, stream 'out_s'.
- //
- make_stream(out_s);
- // Send the RFB message type (CLIENT_CUT_TEXT) to the vnc server.
- init_stream(out_s, clip_bytes + 1 + 3 + 4 + 16);
- out_uint8(out_s, C2S_CLIENT_CUT_TEXT);
- out_uint8s(out_s, 3); // padding
- // Send the length of the cut-text to the vnc server.
- out_uint32_be(out_s, clip_bytes);
- // Send the cut-text (as read from 's') to the vnc server.
- for (index = 0; index < clip_bytes; index++)
- {
- char cur_char = '\0';
- in_uint8(s, cur_char); // text in from 's'
- out_uint8(out_s, cur_char); // text out to 'out_s'
- }
- s_mark_end(out_s);
- lib_send_copy(v, out_s);
- free_stream(out_s);
- break;
-
- default:
- {
- LOG(LOG_LEVEL_DEBUG, "VNC clip information unhandled");
- break;
- }
- }
- }
- else
- {
- LOG(LOG_LEVEL_DEBUG, "lib_process_channel_data: unknown chanid: "
- "%d :(v->clip_chanid) %d", chanid, v->clip_chanid);
- }
-
- return 0;
-}
-
/**************************************************************************//**
* Logs a debug message containing a screen layout
*
@@ -695,11 +495,16 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2,
if ((size >= 0) && (size <= (32 * 1024)) && (data != 0))
{
- init_stream(s, size);
- out_uint8a(s, data, size);
- s_mark_end(s);
- s->p = s->data;
- error = lib_process_channel_data(v, chanid, flags, size, s, total_size);
+ if (chanid == v->clip_chanid)
+ {
+ error = vnc_clip_process_channel_data(v, data, size,
+ total_size, flags);
+ }
+ else
+ {
+ LOG(LOG_LEVEL_DEBUG, "lib_process_channel_data: unknown chanid: "
+ "%d :(v->clip_chanid) %d", chanid, v->clip_chanid);
+ }
}
else
{
@@ -718,7 +523,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2,
{
/* fix for mstsc sending left control down with altgr */
init_stream(s, 8192);
- out_uint8(s, C2S_KEY_EVENT);
+ out_uint8(s, RFB_C2S_KEY_EVENT);
out_uint8(s, 0); /* down flag */
out_uint8s(s, 2);
out_uint32_be(s, 65507); /* left control */
@@ -728,7 +533,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2,
}
init_stream(s, 8192);
- out_uint8(s, C2S_KEY_EVENT);
+ out_uint8(s, RFB_C2S_KEY_EVENT);
out_uint8(s, msg == 15); /* down flag */
out_uint8s(s, 2);
out_uint32_be(s, key);
@@ -780,7 +585,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2,
}
init_stream(s, 8192);
- out_uint8(s, C2S_POINTER_EVENT);
+ out_uint8(s, RFB_C2S_POINTER_EVENT);
out_uint8(s, v->mod_mouse_state);
out_uint16_be(s, param1);
out_uint16_be(s, param2);
@@ -792,7 +597,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2,
if (v->suppress_output == 0)
{
init_stream(s, 8192);
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 0); /* incremental == 0 : Full contents */
x = (param1 >> 16) & 0xffff;
out_uint16_be(s, x);
@@ -1018,15 +823,22 @@ get_bytes_per_pixel(int bpp)
* @param bytes Bytes to skip
* @return != 0 for error
*/
-static int
-skip_trans_bytes(struct trans *trans, int bytes)
+int
+skip_trans_bytes(struct trans *trans, unsigned int bytes)
{
struct stream *s;
- int error;
+ int error = 0;
make_stream(s);
- init_stream(s, bytes);
- error = trans_force_read_s(trans, s, bytes);
+
+ while (error == 0 && bytes > 0)
+ {
+ int chunk_size = MIN(32768, bytes);
+ init_stream(s, chunk_size);
+ error = trans_force_read_s(trans, s, chunk_size);
+ bytes -= chunk_size;
+ }
+
free_stream(s);
return error;
@@ -1054,40 +866,40 @@ skip_encoding(struct vnc *v, int x, int y, int cx, int cy,
switch (encoding)
{
- case ENC_RAW:
+ case RFB_ENC_RAW:
{
int need_size = cx * cy * get_bytes_per_pixel(v->server_bpp);
- LOG(LOG_LEVEL_DEBUG, "Skipping ENC_RAW encoding");
+ LOG(LOG_LEVEL_DEBUG, "Skipping RFB_ENC_RAW encoding");
error = skip_trans_bytes(v->trans, need_size);
}
break;
- case ENC_COPY_RECT:
+ case RFB_ENC_COPY_RECT:
{
- LOG(LOG_LEVEL_DEBUG, "Skipping ENC_COPY_RECT encoding");
+ LOG(LOG_LEVEL_DEBUG, "Skipping RFB_ENC_COPY_RECT encoding");
error = skip_trans_bytes(v->trans, 4);
}
break;
- case ENC_CURSOR:
+ case RFB_ENC_CURSOR:
{
int j = cx * cy * get_bytes_per_pixel(v->server_bpp);
int k = ((cx + 7) / 8) * cy;
- LOG(LOG_LEVEL_DEBUG, "Skipping ENC_CURSOR encoding");
+ LOG(LOG_LEVEL_DEBUG, "Skipping RFB_ENC_CURSOR encoding");
error = skip_trans_bytes(v->trans, j + k);
}
break;
- case ENC_DESKTOP_SIZE:
- LOG(LOG_LEVEL_DEBUG, "Skipping ENC_DESKTOP_SIZE encoding");
+ case RFB_ENC_DESKTOP_SIZE:
+ LOG(LOG_LEVEL_DEBUG, "Skipping RFB_ENC_DESKTOP_SIZE encoding");
break;
- case ENC_EXTENDED_DESKTOP_SIZE:
+ case RFB_ENC_EXTENDED_DESKTOP_SIZE:
{
struct vnc_screen_layout layout = {0};
LOG(LOG_LEVEL_DEBUG,
- "Skipping ENC_EXTENDED_DESKTOP_SIZE encoding "
+ "Skipping RFB_ENC_EXTENDED_DESKTOP_SIZE encoding "
"x=%d, y=%d geom=%dx%d",
x, y, cx, cy);
error = read_extended_desktop_size_rect(v, &layout);
@@ -1165,7 +977,7 @@ find_matching_extended_rect(struct vnc *v,
in_uint16_be(s, cy);
in_uint32_be(s, encoding);
- if (encoding == ENC_EXTENDED_DESKTOP_SIZE &&
+ if (encoding == RFB_ENC_EXTENDED_DESKTOP_SIZE &&
match_layout->s == NULL &&
match(x, y, cx, cy))
{
@@ -1230,7 +1042,7 @@ send_update_request_for_resize_status(struct vnc *v)
/*
* Ask for an immediate, minimal update.
*/
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 0); /* incremental == 0 : Full update */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
@@ -1244,7 +1056,7 @@ send_update_request_for_resize_status(struct vnc *v)
/*
* Ask for a deferred minimal update.
*/
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 1); /* incremental == 1 : Changes only */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
@@ -1260,7 +1072,7 @@ send_update_request_for_resize_status(struct vnc *v)
*/
if (v->suppress_output == 0)
{
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 0); /* incremental == 0 : Full update */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
@@ -1298,20 +1110,6 @@ rect_is_reply_to_us(int x, int y, int cx, int cy)
}
/**************************************************************************//**
- * Returns an error string for an ExtendedDesktopSize status code
- */
-static const char *
-get_eds_status_msg(unsigned int response_code)
-{
- if (response_code >= EDS_STATUS_MSG_COUNT)
- {
- response_code = EDS_STATUS_MSG_COUNT - 1;
- }
-
- return eds_status_msg[response_code];
-}
-
-/**************************************************************************//**
* Handles the first framebuffer update from the server
*
* This is used to determine if the server supports resizes from
@@ -1431,7 +1229,7 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
LOG(LOG_LEVEL_WARNING,
"VNC server resize failed - error code %d [%s]",
response_code,
- get_eds_status_msg(response_code));
+ rfb_get_eds_status_msg(response_code));
/* Force client to same size as server */
LOG(LOG_LEVEL_WARNING, "Resizing client to server %dx%d",
v->server_width, v->server_height);
@@ -1514,7 +1312,7 @@ lib_framebuffer_update(struct vnc *v)
in_uint16_be(s, cy);
in_uint32_be(s, encoding);
- if (encoding == ENC_RAW)
+ if (encoding == RFB_ENC_RAW)
{
need_size = cx * cy * get_bytes_per_pixel(v->server_bpp);
init_stream(pixel_s, need_size);
@@ -1525,7 +1323,7 @@ lib_framebuffer_update(struct vnc *v)
error = v->server_paint_rect(v, x, y, cx, cy, pixel_s->data, cx, cy, 0, 0);
}
}
- else if (encoding == ENC_COPY_RECT)
+ else if (encoding == RFB_ENC_COPY_RECT)
{
init_stream(s, 8192);
error = trans_force_read_s(v->trans, s, 4);
@@ -1537,7 +1335,7 @@ lib_framebuffer_update(struct vnc *v)
error = v->server_screen_blt(v, x, y, cx, cy, srcx, srcy);
}
}
- else if (encoding == ENC_CURSOR)
+ else if (encoding == RFB_ENC_CURSOR)
{
g_memset(cursor_data, 0, 32 * (32 * 3));
g_memset(cursor_mask, 0, 32 * (32 / 8));
@@ -1582,14 +1380,14 @@ lib_framebuffer_update(struct vnc *v)
error = v->server_set_cursor(v, x, y, cursor_data, cursor_mask);
}
}
- else if (encoding == ENC_DESKTOP_SIZE)
+ else if (encoding == RFB_ENC_DESKTOP_SIZE)
{
/* Server end has resized */
v->server_width = cx;
v->server_height = cy;
error = resize_client(v, 1, cx, cy);
}
- else if (encoding == ENC_EXTENDED_DESKTOP_SIZE)
+ else if (encoding == RFB_ENC_EXTENDED_DESKTOP_SIZE)
{
layout.total_width = cx;
layout.total_height = cy;
@@ -1622,7 +1420,7 @@ lib_framebuffer_update(struct vnc *v)
if (v->suppress_output == 0)
{
init_stream(s, 8192);
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 1); /* incremental == 1 : Changes only */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
@@ -1639,57 +1437,6 @@ lib_framebuffer_update(struct vnc *v)
}
/******************************************************************************/
-/* clip data from the vnc server */
-int
-lib_clip_data(struct vnc *v)
-{
- struct stream *s;
- struct stream *out_s;
- int size;
- int error;
-
- free_stream(v->clip_data_s);
- v->clip_data_s = 0;
- make_stream(s);
- init_stream(s, 8192);
- error = trans_force_read_s(v->trans, s, 7);
-
- if (error == 0)
- {
- in_uint8s(s, 3);
- in_uint32_be(s, size);
- make_stream(v->clip_data_s);
- init_stream(v->clip_data_s, size);
- error = trans_force_read_s(v->trans, v->clip_data_s, size);
- }
-
- if (error == 0)
- {
- make_stream(out_s);
- init_stream(out_s, 8192);
- out_uint16_le(out_s, 2);
- out_uint16_le(out_s, 0);
- out_uint32_le(out_s, 0x90);
- out_uint8(out_s, 0x0d);
- out_uint8s(out_s, 35);
- out_uint8(out_s, 0x10);
- out_uint8s(out_s, 35);
- out_uint8(out_s, 0x01);
- out_uint8s(out_s, 35);
- out_uint8(out_s, 0x07);
- out_uint8s(out_s, 35);
- out_uint8s(out_s, 4);
- s_mark_end(out_s);
- size = (int)(out_s->end - out_s->data);
- error = v->server_send_to_channel(v, v->clip_chanid, out_s->data, size, size, 3);
- free_stream(out_s);
- }
-
- free_stream(s);
- return error;
-}
-
-/******************************************************************************/
int
lib_palette_update(struct vnc *v)
{
@@ -1775,7 +1522,7 @@ lib_mod_process_message(struct vnc *v, struct stream *s)
error = 0;
if (error == 0)
{
- if (type == S2C_FRAMEBUFFER_UPDATE)
+ if (type == RFB_S2C_FRAMEBUFFER_UPDATE)
{
switch (v->resize_status)
{
@@ -1791,18 +1538,18 @@ lib_mod_process_message(struct vnc *v, struct stream *s)
error = lib_framebuffer_update(v);
}
}
- else if (type == S2C_SET_COLOUR_MAP_ENTRIES)
+ else if (type == RFB_S2C_SET_COLOUR_MAP_ENTRIES)
{
error = lib_palette_update(v);
}
- else if (type == S2C_BELL)
+ else if (type == RFB_S2C_BELL)
{
error = lib_bell_trigger(v);
}
- else if (type == S2C_SERVER_CUT_TEXT) /* clipboard */
+ else if (type == RFB_S2C_SERVER_CUT_TEXT) /* clipboard */
{
LOG(LOG_LEVEL_DEBUG, "VNC got clip data");
- error = lib_clip_data(v);
+ error = vnc_clip_process_rfb_data(v);
}
else
{
@@ -1828,22 +1575,6 @@ lib_mod_start(struct vnc *v, int w, int h, int bpp)
/******************************************************************************/
static int
-lib_open_clip_channel(struct vnc *v)
-{
- char init_data[12] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- v->clip_chanid = v->server_get_channel_id(v, "cliprdr");
-
- if (v->clip_chanid >= 0)
- {
- v->server_send_to_channel(v, v->clip_chanid, init_data, 12, 12, 3);
- }
-
- return 0;
-}
-
-/******************************************************************************/
-static int
lib_data_in(struct trans *trans)
{
struct vnc *self;
@@ -2117,7 +1848,7 @@ lib_mod_connect(struct vnc *v)
if (error == 0)
{
init_stream(s, 8192);
- out_uint8(s, C2S_SET_PIXEL_FORMAT);
+ out_uint8(s, RFB_C2S_SET_PIXEL_FORMAT);
out_uint8(s, 0);
out_uint8(s, 0);
out_uint8(s, 0);
@@ -2209,13 +1940,13 @@ lib_mod_connect(struct vnc *v)
unsigned int i;
/* These encodings are always supported */
- e[n++] = ENC_RAW;
- e[n++] = ENC_COPY_RECT;
- e[n++] = ENC_CURSOR;
- e[n++] = ENC_DESKTOP_SIZE;
+ e[n++] = RFB_ENC_RAW;
+ e[n++] = RFB_ENC_COPY_RECT;
+ e[n++] = RFB_ENC_CURSOR;
+ e[n++] = RFB_ENC_DESKTOP_SIZE;
if (v->enabled_encodings_mask & MSK_EXTENDED_DESKTOP_SIZE)
{
- e[n++] = ENC_EXTENDED_DESKTOP_SIZE;
+ e[n++] = RFB_ENC_EXTENDED_DESKTOP_SIZE;
}
else
{
@@ -2224,7 +1955,7 @@ lib_mod_connect(struct vnc *v)
}
init_stream(s, 8192);
- out_uint8(s, C2S_SET_ENCODINGS);
+ out_uint8(s, RFB_C2S_SET_ENCODINGS);
out_uint8(s, 0);
out_uint16_be(s, n); /* Number of encodings following */
for (i = 0 ; i < n; ++i)
@@ -2259,7 +1990,7 @@ lib_mod_connect(struct vnc *v)
if (error == 0)
{
v->server_msg(v, "VNC connection complete, connected ok", 0);
- lib_open_clip_channel(v);
+ vnc_clip_open_clip_channel(v);
}
else
{
@@ -2292,7 +2023,6 @@ lib_mod_end(struct vnc *v)
{
}
- free_stream(v->clip_data_s);
return 0;
}
@@ -2452,7 +2182,7 @@ lib_mod_suppress_output(struct vnc *v, int suppress,
{
make_stream(s);
init_stream(s, 8192);
- out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST);
+ out_uint8(s, RFB_C2S_FRAMEBUFFER_UPDATE_REQUEST);
out_uint8(s, 0); /* incremental == 0 : Full contents */
out_uint16_be(s, 0);
out_uint16_be(s, 0);
@@ -2520,6 +2250,8 @@ mod_init(void)
/* Member variables */
v->enabled_encodings_mask = -1;
+ vnc_clip_init(v);
+
return (tintptr) v;
}
@@ -2536,6 +2268,7 @@ mod_exit(tintptr handle)
}
trans_delete(v->trans);
g_free(v->client_layout.s);
+ vnc_clip_exit(v);
g_free(v);
return 0;
}
diff --git a/vnc/vnc.h b/vnc/vnc.h
index 1aeeac05..b34fda67 100644
--- a/vnc/vnc.h
+++ b/vnc/vnc.h
@@ -18,6 +18,9 @@
* libvnc
*/
+#ifndef VNC_H
+#define VNC_H
+
/* include other h files */
#include "arch.h"
#include "parse.h"
@@ -58,6 +61,9 @@ enum vnc_resize_status
struct source_info;
+/* Defined in vnc_clip.c */
+struct vnc_clipboard_data;
+
struct vnc
{
int size; /* size of this struct */
@@ -149,7 +155,7 @@ struct vnc
int shift_state; /* 0 up, 1 down */
int keylayout;
int clip_chanid;
- struct stream *clip_data_s;
+ struct vnc_clipboard_data *vc;
int delay_ms;
struct trans *trans;
int got_guid;
@@ -160,3 +166,13 @@ struct vnc
struct vnc_screen_layout client_layout;
enum vnc_resize_status resize_status;
};
+
+/*
+ * Functions
+ */
+int
+lib_send_copy(struct vnc *v, struct stream *s);
+int
+skip_trans_bytes(struct trans *trans, unsigned int bytes);
+
+#endif /* VNC_H */
diff --git a/vnc/vnc_clip.c b/vnc/vnc_clip.c
new file mode 100644
index 00000000..48d3b4ee
--- /dev/null
+++ b/vnc/vnc_clip.c
@@ -0,0 +1,1031 @@
+
+/* startup_complete is only ever set if we're using the VNC clip facility *//**
+ * xrdp: A Remote Desktop Protocol server.
+ *
+ * Copyright (C) Jay Sorg 2004-2015
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * libvnc
+ *
+ * The message definitions used in this source file can be found mostly
+ * in RFC6143 - "The Remote Framebuffer Protocol".
+ *
+ * The clipboard messages from the RDP client side are mostly
+ * described in [MS-RDPECLIP]
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include <config_ac.h>
+#endif
+
+#include "arch.h"
+#include "vnc.h"
+#include "vnc_clip.h"
+#include "string_calls.h"
+#include "rfb.h"
+#include "log.h"
+#include "trans.h"
+#include "ms-rdpbcgr.h"
+#include "ms-rdpeclip.h"
+#include "xrdp_constants.h"
+
+/**
+ * The formats we advertise as supported to the RDP client
+ */
+static const int
+g_supported_formats[] =
+{
+ CF_UNICODETEXT,
+ CF_LOCALE,
+ CF_TEXT,
+ /* Don't advertise CF_OEMTEXT - anything other than ASCII will be broken */
+ /* CF_OEMTEXT, */
+ 0
+};
+
+/**
+ * Data private to the VNC clipboard
+ *
+ * Note that this doesn't include the clip channel ID, as vnc.c needs
+ * this to redirect virtual channel PDUs to this module */
+struct vnc_clipboard_data
+{
+ struct stream *rfb_clip_s;
+ int requested_clip_format; /* Last requested text format */
+ int active_data_requests; /* Number of outstanding FORMAT_DATA_REQUESTs */
+ struct stream *dechunker_s; /* Dechunker for the RDP clip channel */
+ int capability_version; /* Clipboard virt channel extension version */
+ int capability_flags; /* Channel capability flags */
+ bool_t startup_complete; /* is the startup sequence done (1.3.2.1) */
+};
+
+#ifdef USE_DEVEL_LOGGING
+/***************************************************************************//**
+ * Convert a CF_ constant to text
+ *
+ * @param CF_xxx constant
+ * @param buff Scratchpad for storing a temporary string in
+ * @param bufflen Length of the above
+ *
+ * @return string representation
+ */
+static const char *
+cf2text(int cf, char *buff, int bufflen)
+{
+ const char *result;
+
+ switch (cf)
+ {
+ case CF_UNICODETEXT:
+ result = "CF_UNICODETEXT";
+ break;
+
+ case CF_LOCALE:
+ result = "CF_LOCALE";
+ break;
+
+ case CF_TEXT:
+ result = "CF_TEXT";
+ break;
+
+ case CF_OEMTEXT:
+ result = "CF_OEMTEXT";
+ break;
+
+ default:
+ g_snprintf(buff, bufflen, "CF_<0x%08x>", cf);
+ result = buff;
+ };
+
+ return result;
+}
+#endif /* USE_DEVEL_LOGGING */
+
+/***************************************************************************//**
+ * Adds a CLIPRDR_HEADER ([MS-RDPECLIP] 2.2.1) to the data stream
+ *
+ * The location of the length is stored in the unused 'channel_hdr' field
+ * of the data stream. When send_stream_to_clip_channel() is called,
+ * we can use update the data length.
+ *
+ * @param s Output stream
+ * @param msg_type Message Type
+ * @param msg_flags Message flags
+ */
+static void
+out_cliprdr_header(struct stream *s, int msg_type, int msg_flags)
+{
+ out_uint16_le(s, msg_type);
+ out_uint16_le(s, msg_flags);
+ /* Save the datalen location so we can update it later */
+ s_push_layer(s, channel_hdr, 4);
+}
+
+/***************************************************************************//**
+ * Sends the contents of a stream buffer to the clipboard channel
+ *
+ * Stream contents are chunked appropriately if they are too big to
+ * fit in a single PDU
+ *
+ * The stream object cliprdr datalen header field is updated by this call.
+ *
+ * @param v VNC object
+ * @param s stream buffer
+ *
+ * @pre stream buffer must have been initialised with a call to
+ * out_cliprdr_header()
+ * @pre Stream is terminated with s_mark_end()
+ */
+static int
+send_stream_to_clip_channel(struct vnc *v, struct stream *s)
+{
+ int rv = 0;
+ int datalen = 0; /* cliprdr PDU datalen field */
+ int pos = 0;
+ int pdu_len = 0; /* Length of channel PDU */
+ int total_data_len = (int)(s->end - s->data);
+ int flags;
+ int msg_type;
+ int msg_flags;
+
+ /* Use the pointer saved by out_cliprdr_header() to
+ * write the cliprdr PDU length */
+ s_pop_layer(s, channel_hdr);
+ datalen = (int)(s->end - s->p) - 4;
+ out_uint32_le(s, datalen);
+
+ /* Read the other fields of the cliprdr header for logging */
+ s->p = s->data;
+ in_uint16_le(s, msg_type);
+ in_uint16_le(s, msg_flags);
+ LOG(LOG_LEVEL_DEBUG, "Sending cliprdr PDU type:%s flags:%d datalen:%d",
+ CB_PDUTYPE_TO_STR(msg_type), msg_flags, datalen);
+
+
+ for ( ; rv == 0 && pos < total_data_len ; pos += pdu_len)
+ {
+ pdu_len = MIN(CHANNEL_CHUNK_LENGTH, (total_data_len - pos));
+
+ /* Determine chunking flags for this PDU (MS-RDPBCGR 3.1.5.2.1) */
+ if (pos == 0)
+ {
+ if ((pos + pdu_len) == total_data_len)
+ {
+ /* Only one chunk */
+ flags = (XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_LAST);
+ }
+ else
+ {
+ /* First chunk of several */
+ flags = (XR_CHANNEL_FLAG_FIRST | XR_CHANNEL_FLAG_SHOW_PROTOCOL);
+ }
+ }
+ else if ((pos + pdu_len) == total_data_len)
+ {
+ /* Last chunk of several */
+ flags = (XR_CHANNEL_FLAG_LAST | XR_CHANNEL_FLAG_SHOW_PROTOCOL);
+ }
+ else
+ {
+ /* Intermediate chunk of several */
+ flags = XR_CHANNEL_FLAG_SHOW_PROTOCOL;
+ }
+ rv = v->server_send_to_channel(v, v->clip_chanid,
+ s->data + pos, pdu_len,
+ total_data_len, flags);
+ }
+
+ return rv;
+}
+
+/***************************************************************************//**
+ * Counts the occurrences of a character in a stream
+ * @param s stream
+ * @param c character
+ * @return occurrence count
+ */
+static int
+char_count_in(const struct stream *s, char c)
+{
+ int rv = 0;
+ const char *p = s->data;
+ const char *end = s->end;
+
+ while ((p = g_strnchr(p, c, end - p)) != NULL)
+ {
+ ++rv;
+ ++p; /* Skip counted character */
+ }
+
+ return rv;
+}
+
+/***************************************************************************//**
+ * Searches a Format List PDU for a preferred text format
+ *
+ * On entry, the stream contains a formatListData object
+ *
+ * @param v VNC module
+ * @param msg_flags clipHeader msgFlags field
+ * @params s formatListData object.
+ * @return Preferred text format, or 0 if not found
+ */
+static int
+find_preferred_text_format(struct vnc *v, int msg_flags, struct stream *s)
+{
+ int seen_cf_unicodetext = 0;
+ int seen_cf_text = 0;
+ int format_id;
+#ifdef USE_DEVEL_LOGGING
+ char scratch[64];
+#endif
+
+ while (s_check_rem(s, 4))
+ {
+ in_uint32_le(s, format_id);
+
+ if ((v->vc->capability_flags & CB_USE_LONG_FORMAT_NAMES) == 0)
+ {
+ /* Short format name */
+ int skip = MIN(s_rem(s), 32);
+ in_uint8s(s, skip);
+ }
+ else
+ {
+ /* Skip a wsz string */
+ int wc = 1;
+ while (s_check_rem(s, 2) && wc != 0)
+ {
+ in_uint16_le(s, wc);
+ }
+ }
+
+ LOG_DEVEL(LOG_LEVEL_INFO, "VNC: Format id %s available"
+ " from RDP client",
+ cf2text(format_id, scratch, sizeof(scratch)));
+
+ switch (format_id)
+ {
+ case CF_UNICODETEXT:
+ seen_cf_unicodetext = 1;
+ break;
+
+ case CF_TEXT:
+ seen_cf_text = 1;
+ break;
+ }
+ }
+
+ /* Prefer Unicode (UTF-16), as it's most easily converted to
+ * the ISO-8859-1 supported by the VNC clipboard */
+ return
+ (seen_cf_unicodetext != 0 ? CF_UNICODETEXT :
+ seen_cf_text != 0 ? CF_TEXT :
+ /* Default */ 0);
+}
+
+/******************************************************************************/
+static int
+handle_cb_format_list(struct vnc *v, int msg_flags, struct stream *s)
+{
+ struct stream *out_s;
+ int format;
+ int rv = 0;
+#ifdef USE_DEVEL_LOGGING
+ char scratch[64];
+#endif
+
+ /* This is the last stage of the startup sequence in MS-RDPECLIP 1.3.2.1,
+ * although it does occur at other times */
+ v->vc->startup_complete = 1;
+
+ make_stream(out_s);
+
+ /* Reply to the caller */
+ init_stream(out_s, 64);
+ out_cliprdr_header(out_s, CB_FORMAT_LIST_RESPONSE, CB_RESPONSE_OK);
+ s_mark_end(out_s);
+ send_stream_to_clip_channel(v, out_s);
+
+ /* Send a CB_DATA_REQUEST message to the cliprdr channel,
+ * if a suitable text format is available */
+ if ((format = find_preferred_text_format(v, msg_flags, s)) != 0)
+ {
+ LOG_DEVEL(LOG_LEVEL_INFO,
+ "Asking RDP client for clip data format=%s",
+ cf2text(format, scratch, sizeof(scratch)));
+ v->vc->requested_clip_format = format;
+ ++v->vc->active_data_requests;
+ init_stream(out_s, 64);
+ out_cliprdr_header(out_s, CB_FORMAT_DATA_REQUEST, 0);
+ out_uint32_le(out_s, format);
+ s_mark_end(out_s);
+ send_stream_to_clip_channel(v, out_s);
+ }
+ free_stream(out_s);
+
+ return rv;
+}
+
+/******************************************************************************/
+static int
+handle_cb_format_data_request(struct vnc *v, struct stream *s)
+{
+ int format = 0;
+ struct stream *out_s;
+ int i;
+ struct vnc_clipboard_data *vc = v->vc;
+ int rv = 0;
+ int msg_flags = CB_RESPONSE_OK;
+ int lf_count;
+ int alloclen = 64;
+#ifdef USE_DEVEL_LOGGING
+ char scratch[64];
+#endif
+
+ if (s_check_rem(s, 4))
+ {
+ in_uint32_le(s, format);
+ }
+
+ LOG_DEVEL(LOG_LEVEL_INFO, "RDP client requested data format=%s",
+ cf2text(format, scratch, sizeof(scratch)));
+
+ make_stream(out_s);
+
+ /* For all formats, we need to convert to Windows carriage control,
+ * so we need to know how many '\n' characters become '\r\n' */
+ lf_count = char_count_in(vc->rfb_clip_s, '\n');
+
+ /* If we're writing a big string, we need to increase the alloclen
+ * for the return PDU. We can also vet the requested format here */
+ switch (format)
+ {
+ case CF_TEXT:
+ /* We need to allocate enough characters to hold the string
+ * with '\n' becoming '\r\n' and also for a terminator. */
+ alloclen += vc->rfb_clip_s->size + lf_count + 1;
+ break;
+
+ case CF_UNICODETEXT:
+ /* As CF_TEXT, but twice as much, as each ANSI character maps to
+ * two octets */
+ alloclen += (vc->rfb_clip_s->size + lf_count + 1) * 2;
+ break;
+
+ case CF_LOCALE:
+ break;
+
+ default:
+ /* No idea what this is */
+ msg_flags = CB_RESPONSE_FAIL;
+ }
+
+ /* Allocate the stream and check for failure as the string could be
+ * essentially unlimited in length */
+ init_stream(out_s, alloclen);
+ if (out_s->data == NULL)
+ {
+ LOG(LOG_LEVEL_ERROR,
+ "Memory exhausted allocating %d bytes for clip data response",
+ alloclen);
+ rv = 1;
+ }
+ else
+ {
+ /* Write the packet header.... */
+ out_cliprdr_header(out_s, CB_FORMAT_DATA_RESPONSE, msg_flags);
+
+ /* ...and any data */
+ switch (format)
+ {
+ case CF_LOCALE:
+ /*
+ * This is undocumented.
+ *
+ * Reverse engineering by firing this request off to
+ * a Microsoft client suggests this is a code from
+ * [MS-LCID]. 0x409 maps to en-us which uses codepage
+ * 1252. This is a close enough match to ISO8859-1 as used
+ * by RFB */
+ out_uint32_le(out_s, 0x409);
+ break;
+
+ case CF_TEXT:
+ for (i = 0; i < vc->rfb_clip_s->size; ++i)
+ {
+ char c = vc->rfb_clip_s->data[i];
+ if (c == '\n')
+ {
+ out_uint8(out_s, '\r');
+ }
+ out_uint8(out_s, c);
+ }
+
+ out_uint8s(out_s, 1);
+ break;
+
+ case CF_UNICODETEXT:
+ /* The VNC clipboard format (ISO 8859-1)
+ maps directly to UTF-16LE by moving over the bottom 8 bits,
+ and setting the top 8 bits to zero */
+ for (i = 0; i < vc->rfb_clip_s->size; ++i)
+ {
+ char c = vc->rfb_clip_s->data[i];
+ if (c == '\n')
+ {
+ out_uint8(out_s, '\r');
+ out_uint8(out_s, 0);
+ }
+ out_uint8(out_s, c);
+ out_uint8(out_s, 0);
+ }
+
+ out_uint8s(out_s, 2);
+ break;
+ }
+
+ s_mark_end(out_s);
+ send_stream_to_clip_channel(v, out_s);
+ free_stream(out_s);
+ }
+
+ return rv;
+}
+
+/******************************************************************************/
+static int
+handle_cb_format_data_response(struct vnc *v, struct stream *s)
+{
+ int rv = 0;
+
+ struct vnc_clipboard_data *vc = v->vc;
+
+ /* The [MS-RDPECLIP] specification lets a new CB_FORMAT_LIST PDU turn
+ * up before we've received a response to a CB_FORMAT_DATA_REQUEST.
+ * As a result, there might be more than one CB_FORMAT_DATA_RESPONSE
+ * PDUs in-flight. We handle this by ignoring all but the last PDU
+ * we're expecting
+ */
+ if (vc->active_data_requests > 0 && --vc->active_data_requests == 0)
+ {
+ struct stream *out_s;
+ int length;
+ char c;
+ char lastc;
+ int wc;
+ unsigned int out_of_range = 0;
+
+ /* We've got a copy of the current VNC paste buffer in
+ * vc->rfb_clip_s. Since we're about to change the VNC paste
+ * buffer anyway, we'll use this to construct the ISO8859-1
+ * text, and then send it to the VNC server
+ *
+ * We size the buffer as follows:-
+ * TEXT Use the same size buffer.
+ * UTF-16 - Use half the size
+ *
+ * In all cases this is big enough, or a little over when removal
+ * of `\r` characters is taken into account */
+ if (vc->requested_clip_format == CF_UNICODETEXT)
+ {
+ length = s_rem(s) / 2;
+ }
+ else
+ {
+ length = s_rem(s);
+ }
+
+ init_stream(vc->rfb_clip_s, length);
+ if (vc->rfb_clip_s->data == NULL)
+ {
+ LOG(LOG_LEVEL_ERROR,
+ "Memory exhausted allocating %d bytes for clip buffer",
+ length);
+ rv = 1;
+ }
+ else
+ {
+ switch (vc->requested_clip_format)
+ {
+ case CF_TEXT:
+ lastc = '\0';
+ while (s_check(s))
+ {
+ in_uint8(s, c);
+ if (c == '\n' && lastc == '\r')
+ {
+ /* Overwrite the `\r' */
+ --vc->rfb_clip_s->p;
+ }
+ out_uint8(vc->rfb_clip_s, c);
+ lastc = c;
+ }
+ break;
+
+ case CF_UNICODETEXT:
+ lastc = '\0';
+ while (s_check_rem(s, 2))
+ {
+ in_uint16_le(s, wc);
+ if (wc / 0x100 == 0)
+ {
+ /* Valid ISO8859-1 character in bottom 8 bits */
+ c = wc % 0x100;
+ if (c == '\n' && lastc == '\r')
+ {
+ /* Overwrite the `\r' */
+ --vc->rfb_clip_s->p;
+ }
+ out_uint8(vc->rfb_clip_s, c);
+ lastc = c;
+ }
+ else
+ {
+ /* Character can't be represented in ISO8859-1 */
+ ++out_of_range;
+ if (wc & 0xdc00 && wc <= 0xdfff)
+ {
+ /* Character is start of a surrogate pair */
+ if (s_check_rem(s, 2))
+ {
+ in_uint16_le(s, wc);
+ }
+ }
+ }
+ }
+
+ if (out_of_range > 0)
+ {
+ LOG(LOG_LEVEL_WARNING,
+ "VNC: %u out-of-range Unicode characters"
+ " could not be moved to the VNC clipboard",
+ out_of_range);
+ }
+ break;
+
+ }
+
+ /* Remove a terminator at the end, as RFB doesn't need it */
+ if (vc->rfb_clip_s->p != vc->rfb_clip_s->data &&
+ *(vc->rfb_clip_s->p - 1) == '\0')
+ {
+ --vc->rfb_clip_s->p;
+ }
+ s_mark_end(vc->rfb_clip_s);
+
+ /* Update the VNC server */
+ make_stream(out_s);
+ length = (unsigned int)(vc->rfb_clip_s->end -
+ vc->rfb_clip_s->data);
+ init_stream(out_s, 1 + 3 + 4 + length);
+ out_uint8(out_s, RFB_C2S_CLIENT_CUT_TEXT);
+ out_uint8s(out_s, 3); /* padding */
+ out_uint32_be(out_s, length);
+ out_uint8p(out_s, vc->rfb_clip_s->data, length);
+ s_mark_end(out_s);
+ lib_send_copy(v, out_s);
+ free_stream(out_s);
+ }
+ }
+
+ return rv;
+}
+
+/******************************************************************************/
+static int
+handle_cb_caps(struct vnc *v, struct stream *s)
+{
+ int rv = 0;
+ int i;
+ int capset_count;
+ int capset_type;
+ int capset_length;
+ int version;
+ int flags;
+
+ if (!s_check_rem_and_log(s, 4, "Reading clip capabilities"))
+ {
+ rv = 1;
+ }
+ else
+ {
+ in_uint16_le(s, capset_count);
+ in_uint8s(s, 2); /* pad */
+
+ for (i = 0; i < capset_count && rv == 0; ++i)
+ {
+ if (!s_check_rem_and_log(s, 4, "Reading capability set"))
+ {
+ rv = 1;
+ break;
+ }
+
+ in_uint16_le(s, capset_type); /* Length includes these two fields */
+ in_uint16_le(s, capset_length);
+ capset_length -= 4; /* Account for last two fields */
+
+ switch (capset_type)
+ {
+ case CB_CAPSTYPE_GENERAL:
+ if (!s_check_rem_and_log(s, 8, "Reading general cap set"))
+ {
+ rv = 1;
+ }
+ else
+ {
+ in_uint32_le(s, version); /* version */
+ in_uint32_le(s, flags); /* generalFlags */
+ capset_length -= 8;
+
+ /* Update our own capability fields */
+ if (version > 0 && version < v->vc->capability_version)
+ {
+ v->vc->capability_version = version;
+ }
+ v->vc->capability_flags &= flags;
+
+ LOG(LOG_LEVEL_DEBUG,
+ "Agreed MS-RDPECLIP capability"
+ "version=%d flags=%08x with RDP client",
+ v->vc->capability_version,
+ v->vc->capability_flags);
+ }
+ break;
+
+ default:
+ LOG(LOG_LEVEL_WARNING, "clipboard_process_clip_caps: "
+ "unknown capabilitySetType %d", capset_type);
+ break;
+ }
+
+ /* Check for padding at the end of the set */
+ if (capset_length > 0)
+ {
+ if (!s_check_rem_and_log(s, capset_length, "cap set padding"))
+ {
+ rv = 1;
+ }
+ else
+ {
+ in_uint8s(s, capset_length);
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+/***************************************************************************//**
+ * Send a format list PDU to the RDP client
+ *
+ * Described in [MS-RDPECLIP] 2.2.3.1
+ *
+ * @param v VNC structure
+ */
+static void
+send_format_list(struct vnc *v)
+{
+ struct vnc_clipboard_data *vc = v->vc;
+ int use_long_names = vc->capability_flags & CB_USE_LONG_FORMAT_NAMES;
+ struct stream *out_s;
+ unsigned int i = 0;
+ int format;
+
+ make_stream(out_s);
+ init_stream(out_s, 8192);
+ out_cliprdr_header(out_s, CB_FORMAT_LIST, use_long_names);
+
+ while ((format = g_supported_formats[i++]) != 0)
+ {
+ if (use_long_names)
+ {
+ /* Long format name [MS-RDPECLIP] 2.2.3.1.2.1 */
+ out_uint32_le(out_s, format);
+ out_uint8s(out_s, 2); /* wsz terminator */
+ }
+ else
+ {
+ /* Short format name [MS-RDPECLIP] 2.2.3.1.1.1 */
+ out_uint32_le(out_s, format);
+ out_uint8s(out_s, 32);
+ }
+ }
+ s_mark_end(out_s);
+ send_stream_to_clip_channel(v, out_s);
+ free_stream(out_s);
+}
+
+/******************************************************************************/
+void
+vnc_clip_init(struct vnc *v)
+{
+ v->vc = (struct vnc_clipboard_data *)g_malloc(sizeof(*v->vc), 1);
+ make_stream(v->vc->rfb_clip_s);
+}
+
+/******************************************************************************/
+void
+vnc_clip_exit(struct vnc *v)
+{
+ if (v != NULL && v->vc != NULL)
+ {
+ free_stream(v->vc->rfb_clip_s);
+ free_stream(v->vc->dechunker_s);
+ g_free(v->vc);
+ }
+}
+
+
+/******************************************************************************/
+int
+vnc_clip_process_eclip_pdu(struct vnc *v, struct stream *s)
+{
+ int type;
+ int msg_flags;
+ int datalen;
+ int rv = 0;
+
+ /* Ignore PDUs with no complete header */
+ if (s_check_rem_and_log(s, 8, "MS-RDPECLIP PDU Header"))
+ {
+ in_uint16_le(s, type);
+ in_uint16_le(s, msg_flags);
+ in_uint32_le(s, datalen);
+
+ LOG(LOG_LEVEL_DEBUG, "got clip data type %s msg_flags %d length %d",
+ CB_PDUTYPE_TO_STR(type), msg_flags, datalen);
+ LOG_DEVEL_HEXDUMP(LOG_LEVEL_TRACE, "clipboard data",
+ s->p, s->end - s->p);
+
+ /* Check the PDU is contained in the stream */
+ if (!s_check_rem_and_log(s, datalen, "MS-RDPECLIP PDU"))
+ {
+ datalen = s_rem(s);
+ }
+ else
+ {
+ /* Truncate the PDU to the data length so we can use the
+ * normal functions to parse the PDU */
+ s->end = s->p + datalen;
+ }
+
+ switch (type)
+ {
+ case CB_FORMAT_LIST:
+ rv = handle_cb_format_list(v, msg_flags, s);
+ break;
+
+ case CB_FORMAT_LIST_RESPONSE:
+ /* We don't need to do anything with this */
+ break;
+
+ case CB_FORMAT_DATA_REQUEST:
+ rv = handle_cb_format_data_request(v, s);
+ break;
+
+ case CB_FORMAT_DATA_RESPONSE:
+ if (msg_flags == CB_RESPONSE_OK)
+ {
+ rv = handle_cb_format_data_response(v, s);
+ }
+ break;
+
+ case CB_CLIP_CAPS:
+ rv = handle_cb_caps(v, s);
+ break;
+ }
+ }
+
+ return rv;
+}
+
+
+/******************************************************************************/
+/**
+ * Process a [MS-RDPBCGR] 2.2.6.1 Virtual Channel PDU and re-assemble the
+ * data chunks as needed - see 3.1.5.2.2.1
+ */
+int
+vnc_clip_process_channel_data(struct vnc *v, char *data, int size,
+ int total_size, int flags)
+{
+ int rv = 1;
+ struct vnc_clipboard_data *vc = v->vc;
+ bool_t first = ((flags & XR_CHANNEL_FLAG_FIRST) != 0);
+ bool_t last = ((flags & XR_CHANNEL_FLAG_LAST) != 0);
+
+ if (size > total_size)
+ {
+ /* This should never happen */
+ LOG(LOG_LEVEL_ERROR,
+ "Ignoring bad PDU chunk data on clip channel");
+ }
+ else if (first && vc->dechunker_s != NULL)
+ {
+ /*
+ * If this packet is marked as 'first', we should not be
+ * dechunking data already */
+ LOG(LOG_LEVEL_ERROR, "Packet chunking start state error");
+ free_stream(vc->dechunker_s);
+ vc->dechunker_s = NULL;
+ }
+ else if (!first && vc->dechunker_s == NULL)
+ {
+ /*
+ * This is not the first packet, but the dechunker is not active */
+ LOG(LOG_LEVEL_ERROR, "Packet chunking end state error");
+ }
+ else if (first && last)
+ {
+ /* this is a complete packet
+ * Construct a temp stream for the complete packet, and pass it
+ * to the application */
+ struct stream packet_s = {0};
+
+ packet_s.data = data;
+ packet_s.size = size;
+ packet_s.end = packet_s.data + size;
+ packet_s.p = packet_s.data;
+ rv = vnc_clip_process_eclip_pdu(v, &packet_s);
+ }
+ else if (first)
+ {
+ /* Start de-chunking the data */
+ make_stream(vc->dechunker_s);
+ init_stream(vc->dechunker_s, (int)total_size);
+
+ /* MS-RDPBCGR 3.1.5.2.2.1 states:-
+ *
+ * A reassembly buffer MUST be created by the virtual channel
+ * endpoint using the size specified by totalLength when
+ * the first chunk is received.
+ *
+ * The 'total_size' can be several GB in size, so we really need
+ * to check for an allocation failure here */
+ if (vc->dechunker_s->data == NULL)
+ {
+ LOG(LOG_LEVEL_ERROR,
+ "Memory exhausted dechunking a %u byte virtual channel PDU",
+ total_size);
+ }
+ else
+ {
+ out_uint8a(vc->dechunker_s, data, size);
+ rv = 0;
+ }
+ }
+ else if (s_check_rem_out_and_log(vc->dechunker_s,
+ size, "VNC dechunker:"))
+ {
+ out_uint8a(vc->dechunker_s, data, size);
+ /* At the end? */
+ if (last)
+ {
+ s_mark_end(vc->dechunker_s);
+ vc->dechunker_s->p = vc->dechunker_s->data;
+
+ /* Call the app and reset the dechunker */
+ rv = vnc_clip_process_eclip_pdu(v, vc->dechunker_s);
+ free_stream(vc->dechunker_s);
+ vc->dechunker_s = NULL;
+ }
+ else
+ {
+ rv = 0;
+ }
+ }
+
+ return rv;
+}
+
+/******************************************************************************/
+/* clip data from the vnc server */
+int
+vnc_clip_process_rfb_data(struct vnc *v)
+{
+ struct vnc_clipboard_data *vc = v->vc;
+ struct stream *s;
+ int size;
+ int rv;
+
+ make_stream(s);
+ init_stream(s, 8192);
+ rv = trans_force_read_s(v->trans, s, 7);
+
+ if (rv == 0)
+ {
+ in_uint8s(s, 3);
+ in_uint32_be(s, size);
+
+ if (v->clip_chanid < 0 || v->server_chansrv_in_use(v))
+ {
+ /* Skip this message */
+ LOG(LOG_LEVEL_DEBUG, "Skipping %d clip bytes from RFB", size);
+ rv = skip_trans_bytes(v->trans, size);
+ }
+ else
+ {
+ /* Lose any existing RFB clip data */
+ free_stream(vc->rfb_clip_s);
+ vc->rfb_clip_s = 0;
+
+ make_stream(vc->rfb_clip_s);
+ if (size >= 0)
+ {
+ init_stream(vc->rfb_clip_s, size);
+ }
+ if (vc->rfb_clip_s->data == NULL)
+ {
+ LOG(LOG_LEVEL_ERROR,
+ "Memory exhausted allocating %d bytes for RFB clip data",
+ size);
+ rv = 1;
+ }
+ else
+ {
+ LOG(LOG_LEVEL_DEBUG, "Reading %d clip bytes from RFB", size);
+ rv = trans_force_read_s(v->trans, vc->rfb_clip_s, size);
+ }
+ }
+ }
+
+ /* startup_complete is only ever set if we're using the VNC clip facility */
+ if (rv == 0 && vc->startup_complete)
+ {
+ send_format_list(v);
+ }
+
+ free_stream(s);
+ return rv;
+}
+
+/******************************************************************************/
+int
+vnc_clip_open_clip_channel(struct vnc *v)
+{
+ v->clip_chanid = v->server_get_channel_id(v, "cliprdr");
+
+ if (v->server_chansrv_in_use(v))
+ {
+ /*
+ * The clipboard is provided by chansrv, if at all - it may of
+ * course be disabled there.
+ */
+ LOG(LOG_LEVEL_INFO, "VNC: Clipboard (if available) is provided "
+ "by chansrv facility");
+ }
+ else if (v->clip_chanid < 0)
+ {
+ LOG(LOG_LEVEL_INFO, "VNC: Clipboard is unavailable");
+ }
+ else
+ {
+ struct stream *s;
+
+ LOG(LOG_LEVEL_INFO, "VNC: Clipboard supports ISO-8859-1 text only");
+
+ make_stream(s);
+ init_stream(s, 8192);
+
+ v->vc->capability_version = CB_CAPS_VERSION_2;
+ v->vc->capability_flags = CB_USE_LONG_FORMAT_NAMES;
+ /**
+ * Send two PDUs to initialise the channel. The client should
+ * respond with a CB_CLIP_CAPS PDU of its own. See [MS-RDPECLIP]
+ * 1.3.2.1 */
+ out_cliprdr_header(s, CB_CLIP_CAPS, 0);
+ out_uint16_le(s, 1); /* #cCapabilitiesSets */
+ out_uint16_le(s, 0); /* pad1 */
+ /* CLIPRDR_GENERAL_CAPABILITY */
+ out_uint16_le(s, CB_CAPSTYPE_GENERAL); /* capabilitySetType */
+ out_uint16_le(s, 12); /* lengthCapability */
+ out_uint32_le(s, v->vc->capability_version);
+ out_uint32_le(s, v->vc->capability_flags);
+ s_mark_end(s);
+ send_stream_to_clip_channel(v, s);
+
+ /* Send the monitor ready PDU */
+ init_stream(s, 0);
+ out_cliprdr_header(s, CB_MONITOR_READY, 0);
+ s_mark_end(s);
+ send_stream_to_clip_channel(v, s);
+
+ /* Need to complete the startup handshake before we send formats */
+ v->vc->startup_complete = 1;
+ }
+
+ return 0;
+}
diff --git a/vnc/vnc_clip.h b/vnc/vnc_clip.h
new file mode 100644
index 00000000..3bab382b
--- /dev/null
+++ b/vnc/vnc_clip.h
@@ -0,0 +1,70 @@
+/**
+ * xrdp: A Remote Desktop Protocol server.
+ *
+ * Copyright (C) Jay Sorg 2004-2015
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * libvnc - functions used by the VNC clipboard feature
+ */
+
+#ifndef VNC_CLIP_H
+#define VNC_CLIP_H
+
+struct vnc;
+struct stream;
+
+/**
+ * Init the clip module private data structures
+ */
+void
+vnc_clip_init(struct vnc *v);
+
+/**
+ * Deallocate the clip module private data structures
+ */
+void
+vnc_clip_exit(struct vnc *v);
+
+/**
+ * Process incoming data from the RDP clip channel
+ * @param v VNC Object
+ * @param s Stream object containing data
+ *
+ * @return Non-zero if error occurs
+ */
+int
+vnc_clip_process_channel_data(struct vnc *v, char *data, int size,
+ int total_size, int flags);
+
+/**
+ * Process incoming RFB protocol clipboard data
+ * @param v VNC Object
+ *
+ * @return Non-zero if error occurs
+ */
+int
+vnc_clip_process_rfb_data(struct vnc *v);
+
+/**
+ * Open the RDP clipboard channel
+ *
+ * The clip channel ID is written to the VNC object
+ * *
+ * @param v VNC Object
+ * @return Non-zero if error occurs
+ */
+int
+vnc_clip_open_clip_channel(struct vnc *v);
+
+#endif /* VNC_CLIP_H */