1 /* 2 * QEMU VNC display driver -- clipboard support 3 * 4 * Copyright (C) 2021 Gerd Hoffmann <kraxel@redhat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qemu-common.h" 27 #include "vnc.h" 28 #include "vnc-jobs.h" 29 30 static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) 31 { 32 z_stream stream = { 33 .next_in = in, 34 .avail_in = in_len, 35 .zalloc = Z_NULL, 36 .zfree = Z_NULL, 37 }; 38 uint32_t out_len = 8; 39 uint8_t *out = g_malloc(out_len); 40 int ret; 41 42 stream.next_out = out + stream.total_out; 43 stream.avail_out = out_len - stream.total_out; 44 45 ret = inflateInit(&stream); 46 if (ret != Z_OK) { 47 goto err; 48 } 49 50 while (stream.avail_in) { 51 ret = inflate(&stream, Z_FINISH); 52 switch (ret) { 53 case Z_OK: 54 case Z_STREAM_END: 55 break; 56 case Z_BUF_ERROR: 57 out_len <<= 1; 58 if (out_len > (1 << 20)) { 59 goto err_end; 60 } 61 out = g_realloc(out, out_len); 62 stream.next_out = out + stream.total_out; 63 stream.avail_out = out_len - stream.total_out; 64 break; 65 default: 66 goto err_end; 67 } 68 } 69 70 *size = stream.total_out; 71 inflateEnd(&stream); 72 73 return out; 74 75 err_end: 76 inflateEnd(&stream); 77 err: 78 g_free(out); 79 return NULL; 80 } 81 82 static uint8_t *deflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) 83 { 84 z_stream stream = { 85 .next_in = in, 86 .avail_in = in_len, 87 .zalloc = Z_NULL, 88 .zfree = Z_NULL, 89 }; 90 uint32_t out_len = 8; 91 uint8_t *out = g_malloc(out_len); 92 int ret; 93 94 stream.next_out = out + stream.total_out; 95 stream.avail_out = out_len - stream.total_out; 96 97 ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION); 98 if (ret != Z_OK) { 99 goto err; 100 } 101 102 while (ret != Z_STREAM_END) { 103 ret = deflate(&stream, Z_FINISH); 104 switch (ret) { 105 case Z_OK: 106 case Z_STREAM_END: 107 break; 108 case Z_BUF_ERROR: 109 out_len <<= 1; 110 if (out_len > (1 << 20)) { 111 goto err_end; 112 } 113 out = g_realloc(out, out_len); 114 stream.next_out = out + stream.total_out; 115 stream.avail_out = out_len - stream.total_out; 116 break; 117 default: 118 goto err_end; 119 } 120 } 121 122 *size = stream.total_out; 123 deflateEnd(&stream); 124 125 return out; 126 127 err_end: 128 deflateEnd(&stream); 129 err: 130 g_free(out); 131 return NULL; 132 } 133 134 static void vnc_clipboard_send(VncState *vs, uint32_t count, uint32_t *dwords) 135 { 136 int i; 137 138 vnc_lock_output(vs); 139 vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT); 140 vnc_write_u8(vs, 0); 141 vnc_write_u8(vs, 0); 142 vnc_write_u8(vs, 0); 143 vnc_write_s32(vs, -(count * sizeof(uint32_t))); /* -(message length) */ 144 for (i = 0; i < count; i++) { 145 vnc_write_u32(vs, dwords[i]); 146 } 147 vnc_unlock_output(vs); 148 vnc_flush(vs); 149 } 150 151 static void vnc_clipboard_provide(VncState *vs, 152 QemuClipboardInfo *info, 153 QemuClipboardType type) 154 { 155 uint32_t flags = 0; 156 g_autofree uint8_t *buf = NULL; 157 g_autofree void *zbuf = NULL; 158 uint32_t zsize; 159 160 switch (type) { 161 case QEMU_CLIPBOARD_TYPE_TEXT: 162 flags |= VNC_CLIPBOARD_TEXT; 163 break; 164 default: 165 return; 166 } 167 flags |= VNC_CLIPBOARD_PROVIDE; 168 169 buf = g_malloc(info->types[type].size + 4); 170 buf[0] = (info->types[type].size >> 24) & 0xff; 171 buf[1] = (info->types[type].size >> 16) & 0xff; 172 buf[2] = (info->types[type].size >> 8) & 0xff; 173 buf[3] = (info->types[type].size >> 0) & 0xff; 174 memcpy(buf + 4, info->types[type].data, info->types[type].size); 175 zbuf = deflate_buffer(buf, info->types[type].size + 4, &zsize); 176 if (!zbuf) { 177 return; 178 } 179 180 vnc_lock_output(vs); 181 vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT); 182 vnc_write_u8(vs, 0); 183 vnc_write_u8(vs, 0); 184 vnc_write_u8(vs, 0); 185 vnc_write_s32(vs, -(sizeof(uint32_t) + zsize)); /* -(message length) */ 186 vnc_write_u32(vs, flags); 187 vnc_write(vs, zbuf, zsize); 188 vnc_unlock_output(vs); 189 vnc_flush(vs); 190 } 191 192 static void vnc_clipboard_update_info(VncState *vs, QemuClipboardInfo *info) 193 { 194 QemuClipboardType type; 195 bool self_update = info->owner == &vs->cbpeer; 196 uint32_t flags = 0; 197 198 if (info != vs->cbinfo) { 199 qemu_clipboard_info_unref(vs->cbinfo); 200 vs->cbinfo = qemu_clipboard_info_ref(info); 201 vs->cbpending = 0; 202 if (!self_update) { 203 if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) { 204 flags |= VNC_CLIPBOARD_TEXT; 205 } 206 flags |= VNC_CLIPBOARD_NOTIFY; 207 vnc_clipboard_send(vs, 1, &flags); 208 } 209 return; 210 } 211 212 if (self_update) { 213 return; 214 } 215 216 for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { 217 if (vs->cbpending & (1 << type)) { 218 vs->cbpending &= ~(1 << type); 219 vnc_clipboard_provide(vs, info, type); 220 } 221 } 222 } 223 224 static void vnc_clipboard_notify(Notifier *notifier, void *data) 225 { 226 VncState *vs = container_of(notifier, VncState, cbpeer.notifier); 227 QemuClipboardNotify *notify = data; 228 229 switch (notify->type) { 230 case QEMU_CLIPBOARD_UPDATE_INFO: 231 vnc_clipboard_update_info(vs, notify->info); 232 return; 233 case QEMU_CLIPBOARD_RESET_SERIAL: 234 /* ignore */ 235 return; 236 } 237 } 238 239 static void vnc_clipboard_request(QemuClipboardInfo *info, 240 QemuClipboardType type) 241 { 242 VncState *vs = container_of(info->owner, VncState, cbpeer); 243 uint32_t flags = 0; 244 245 if (type == QEMU_CLIPBOARD_TYPE_TEXT) { 246 flags |= VNC_CLIPBOARD_TEXT; 247 } 248 if (!flags) { 249 return; 250 } 251 flags |= VNC_CLIPBOARD_REQUEST; 252 253 vnc_clipboard_send(vs, 1, &flags); 254 } 255 256 void vnc_client_cut_text_ext(VncState *vs, int32_t len, uint32_t flags, uint8_t *data) 257 { 258 if (flags & VNC_CLIPBOARD_CAPS) { 259 /* need store caps somewhere ? */ 260 return; 261 } 262 263 if (flags & VNC_CLIPBOARD_NOTIFY) { 264 QemuClipboardInfo *info = 265 qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD); 266 if (flags & VNC_CLIPBOARD_TEXT) { 267 info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true; 268 } 269 qemu_clipboard_update(info); 270 qemu_clipboard_info_unref(info); 271 return; 272 } 273 274 if (flags & VNC_CLIPBOARD_PROVIDE && 275 vs->cbinfo && 276 vs->cbinfo->owner == &vs->cbpeer) { 277 uint32_t size = 0; 278 g_autofree uint8_t *buf = inflate_buffer(data, len - 4, &size); 279 if ((flags & VNC_CLIPBOARD_TEXT) && 280 buf && size >= 4) { 281 uint32_t tsize = read_u32(buf, 0); 282 uint8_t *tbuf = buf + 4; 283 if (tsize < size) { 284 qemu_clipboard_set_data(&vs->cbpeer, vs->cbinfo, 285 QEMU_CLIPBOARD_TYPE_TEXT, 286 tsize, tbuf, true); 287 } 288 } 289 } 290 291 if (flags & VNC_CLIPBOARD_REQUEST && 292 vs->cbinfo && 293 vs->cbinfo->owner != &vs->cbpeer) { 294 if ((flags & VNC_CLIPBOARD_TEXT) && 295 vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available) { 296 if (vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].data) { 297 vnc_clipboard_provide(vs, vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT); 298 } else { 299 vs->cbpending |= (1 << QEMU_CLIPBOARD_TYPE_TEXT); 300 qemu_clipboard_request(vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT); 301 } 302 } 303 } 304 } 305 306 void vnc_client_cut_text(VncState *vs, size_t len, uint8_t *text) 307 { 308 QemuClipboardInfo *info = 309 qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD); 310 311 qemu_clipboard_set_data(&vs->cbpeer, info, QEMU_CLIPBOARD_TYPE_TEXT, 312 len, text, true); 313 qemu_clipboard_info_unref(info); 314 } 315 316 void vnc_server_cut_text_caps(VncState *vs) 317 { 318 uint32_t caps[2]; 319 320 if (!vnc_has_feature(vs, VNC_FEATURE_CLIPBOARD_EXT)) { 321 return; 322 } 323 324 caps[0] = (VNC_CLIPBOARD_PROVIDE | 325 VNC_CLIPBOARD_NOTIFY | 326 VNC_CLIPBOARD_REQUEST | 327 VNC_CLIPBOARD_CAPS | 328 VNC_CLIPBOARD_TEXT); 329 caps[1] = 0; 330 vnc_clipboard_send(vs, 2, caps); 331 332 if (!vs->cbpeer.notifier.notify) { 333 vs->cbpeer.name = "vnc"; 334 vs->cbpeer.notifier.notify = vnc_clipboard_notify; 335 vs->cbpeer.request = vnc_clipboard_request; 336 qemu_clipboard_peer_register(&vs->cbpeer); 337 } 338 } 339