vnc-clipboard.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. #include "qemu/osdep.h"
  25. #include "vnc.h"
  26. #include "vnc-jobs.h"
  27. static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
  28. {
  29. z_stream stream = {
  30. .next_in = in,
  31. .avail_in = in_len,
  32. .zalloc = Z_NULL,
  33. .zfree = Z_NULL,
  34. };
  35. uint32_t out_len = 8;
  36. uint8_t *out = g_malloc(out_len);
  37. int ret;
  38. stream.next_out = out + stream.total_out;
  39. stream.avail_out = out_len - stream.total_out;
  40. ret = inflateInit(&stream);
  41. if (ret != Z_OK) {
  42. goto err;
  43. }
  44. while (stream.avail_in) {
  45. ret = inflate(&stream, Z_FINISH);
  46. switch (ret) {
  47. case Z_OK:
  48. break;
  49. case Z_STREAM_END:
  50. *size = stream.total_out;
  51. inflateEnd(&stream);
  52. return out;
  53. case Z_BUF_ERROR:
  54. out_len <<= 1;
  55. if (out_len > (1 << 20)) {
  56. goto err_end;
  57. }
  58. out = g_realloc(out, out_len);
  59. stream.next_out = out + stream.total_out;
  60. stream.avail_out = out_len - stream.total_out;
  61. break;
  62. default:
  63. goto err_end;
  64. }
  65. }
  66. *size = stream.total_out;
  67. inflateEnd(&stream);
  68. return out;
  69. err_end:
  70. inflateEnd(&stream);
  71. err:
  72. g_free(out);
  73. return NULL;
  74. }
  75. static uint8_t *deflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size)
  76. {
  77. z_stream stream = {
  78. .next_in = in,
  79. .avail_in = in_len,
  80. .zalloc = Z_NULL,
  81. .zfree = Z_NULL,
  82. };
  83. uint32_t out_len = 8;
  84. uint8_t *out = g_malloc(out_len);
  85. int ret;
  86. stream.next_out = out + stream.total_out;
  87. stream.avail_out = out_len - stream.total_out;
  88. ret = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
  89. if (ret != Z_OK) {
  90. goto err;
  91. }
  92. while (ret != Z_STREAM_END) {
  93. ret = deflate(&stream, Z_FINISH);
  94. switch (ret) {
  95. case Z_OK:
  96. case Z_STREAM_END:
  97. break;
  98. case Z_BUF_ERROR:
  99. out_len <<= 1;
  100. if (out_len > (1 << 20)) {
  101. goto err_end;
  102. }
  103. out = g_realloc(out, out_len);
  104. stream.next_out = out + stream.total_out;
  105. stream.avail_out = out_len - stream.total_out;
  106. break;
  107. default:
  108. goto err_end;
  109. }
  110. }
  111. *size = stream.total_out;
  112. deflateEnd(&stream);
  113. return out;
  114. err_end:
  115. deflateEnd(&stream);
  116. err:
  117. g_free(out);
  118. return NULL;
  119. }
  120. static void vnc_clipboard_send(VncState *vs, uint32_t count, uint32_t *dwords)
  121. {
  122. int i;
  123. vnc_lock_output(vs);
  124. vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT);
  125. vnc_write_u8(vs, 0);
  126. vnc_write_u8(vs, 0);
  127. vnc_write_u8(vs, 0);
  128. vnc_write_s32(vs, -(count * sizeof(uint32_t))); /* -(message length) */
  129. for (i = 0; i < count; i++) {
  130. vnc_write_u32(vs, dwords[i]);
  131. }
  132. vnc_unlock_output(vs);
  133. vnc_flush(vs);
  134. }
  135. static void vnc_clipboard_provide(VncState *vs,
  136. QemuClipboardInfo *info,
  137. QemuClipboardType type)
  138. {
  139. uint32_t flags = 0;
  140. g_autofree uint8_t *buf = NULL;
  141. g_autofree void *zbuf = NULL;
  142. uint32_t zsize;
  143. switch (type) {
  144. case QEMU_CLIPBOARD_TYPE_TEXT:
  145. flags |= VNC_CLIPBOARD_TEXT;
  146. break;
  147. default:
  148. return;
  149. }
  150. flags |= VNC_CLIPBOARD_PROVIDE;
  151. buf = g_malloc(info->types[type].size + 4);
  152. buf[0] = (info->types[type].size >> 24) & 0xff;
  153. buf[1] = (info->types[type].size >> 16) & 0xff;
  154. buf[2] = (info->types[type].size >> 8) & 0xff;
  155. buf[3] = (info->types[type].size >> 0) & 0xff;
  156. memcpy(buf + 4, info->types[type].data, info->types[type].size);
  157. zbuf = deflate_buffer(buf, info->types[type].size + 4, &zsize);
  158. if (!zbuf) {
  159. return;
  160. }
  161. vnc_lock_output(vs);
  162. vnc_write_u8(vs, VNC_MSG_SERVER_CUT_TEXT);
  163. vnc_write_u8(vs, 0);
  164. vnc_write_u8(vs, 0);
  165. vnc_write_u8(vs, 0);
  166. vnc_write_s32(vs, -(sizeof(uint32_t) + zsize)); /* -(message length) */
  167. vnc_write_u32(vs, flags);
  168. vnc_write(vs, zbuf, zsize);
  169. vnc_unlock_output(vs);
  170. vnc_flush(vs);
  171. }
  172. static void vnc_clipboard_update_info(VncState *vs, QemuClipboardInfo *info)
  173. {
  174. QemuClipboardType type;
  175. bool self_update = info->owner == &vs->cbpeer;
  176. uint32_t flags = 0;
  177. if (info != vs->cbinfo) {
  178. qemu_clipboard_info_unref(vs->cbinfo);
  179. vs->cbinfo = qemu_clipboard_info_ref(info);
  180. vs->cbpending = 0;
  181. if (!self_update) {
  182. if (info->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
  183. flags |= VNC_CLIPBOARD_TEXT;
  184. }
  185. flags |= VNC_CLIPBOARD_NOTIFY;
  186. vnc_clipboard_send(vs, 1, &flags);
  187. }
  188. return;
  189. }
  190. if (self_update) {
  191. return;
  192. }
  193. for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
  194. if (vs->cbpending & (1 << type)) {
  195. vs->cbpending &= ~(1 << type);
  196. vnc_clipboard_provide(vs, info, type);
  197. }
  198. }
  199. }
  200. static void vnc_clipboard_notify(Notifier *notifier, void *data)
  201. {
  202. VncState *vs = container_of(notifier, VncState, cbpeer.notifier);
  203. QemuClipboardNotify *notify = data;
  204. switch (notify->type) {
  205. case QEMU_CLIPBOARD_UPDATE_INFO:
  206. vnc_clipboard_update_info(vs, notify->info);
  207. return;
  208. case QEMU_CLIPBOARD_RESET_SERIAL:
  209. /* ignore */
  210. return;
  211. }
  212. }
  213. static void vnc_clipboard_request(QemuClipboardInfo *info,
  214. QemuClipboardType type)
  215. {
  216. VncState *vs = container_of(info->owner, VncState, cbpeer);
  217. uint32_t flags = 0;
  218. if (type == QEMU_CLIPBOARD_TYPE_TEXT) {
  219. flags |= VNC_CLIPBOARD_TEXT;
  220. }
  221. if (!flags) {
  222. return;
  223. }
  224. flags |= VNC_CLIPBOARD_REQUEST;
  225. vnc_clipboard_send(vs, 1, &flags);
  226. }
  227. void vnc_client_cut_text_ext(VncState *vs, int32_t len, uint32_t flags, uint8_t *data)
  228. {
  229. if (flags & VNC_CLIPBOARD_CAPS) {
  230. /* need store caps somewhere ? */
  231. return;
  232. }
  233. if (flags & VNC_CLIPBOARD_NOTIFY) {
  234. QemuClipboardInfo *info =
  235. qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
  236. if (flags & VNC_CLIPBOARD_TEXT) {
  237. info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
  238. }
  239. qemu_clipboard_update(info);
  240. qemu_clipboard_info_unref(info);
  241. return;
  242. }
  243. if (flags & VNC_CLIPBOARD_PROVIDE &&
  244. vs->cbinfo &&
  245. vs->cbinfo->owner == &vs->cbpeer) {
  246. uint32_t size = 0;
  247. g_autofree uint8_t *buf = inflate_buffer(data, len - 4, &size);
  248. if ((flags & VNC_CLIPBOARD_TEXT) &&
  249. buf && size >= 4) {
  250. uint32_t tsize = read_u32(buf, 0);
  251. uint8_t *tbuf = buf + 4;
  252. if (tsize < size) {
  253. qemu_clipboard_set_data(&vs->cbpeer, vs->cbinfo,
  254. QEMU_CLIPBOARD_TYPE_TEXT,
  255. tsize, tbuf, true);
  256. }
  257. }
  258. }
  259. if (flags & VNC_CLIPBOARD_REQUEST &&
  260. vs->cbinfo &&
  261. vs->cbinfo->owner != &vs->cbpeer) {
  262. if ((flags & VNC_CLIPBOARD_TEXT) &&
  263. vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available) {
  264. if (vs->cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].data) {
  265. vnc_clipboard_provide(vs, vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT);
  266. } else {
  267. vs->cbpending |= (1 << QEMU_CLIPBOARD_TYPE_TEXT);
  268. qemu_clipboard_request(vs->cbinfo, QEMU_CLIPBOARD_TYPE_TEXT);
  269. }
  270. }
  271. }
  272. }
  273. void vnc_client_cut_text(VncState *vs, size_t len, uint8_t *text)
  274. {
  275. QemuClipboardInfo *info =
  276. qemu_clipboard_info_new(&vs->cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
  277. qemu_clipboard_set_data(&vs->cbpeer, info, QEMU_CLIPBOARD_TYPE_TEXT,
  278. len, text, true);
  279. qemu_clipboard_info_unref(info);
  280. }
  281. void vnc_server_cut_text_caps(VncState *vs)
  282. {
  283. uint32_t caps[2];
  284. if (!vnc_has_feature(vs, VNC_FEATURE_CLIPBOARD_EXT)) {
  285. return;
  286. }
  287. caps[0] = (VNC_CLIPBOARD_PROVIDE |
  288. VNC_CLIPBOARD_NOTIFY |
  289. VNC_CLIPBOARD_REQUEST |
  290. VNC_CLIPBOARD_CAPS |
  291. VNC_CLIPBOARD_TEXT);
  292. caps[1] = 0;
  293. vnc_clipboard_send(vs, 2, caps);
  294. if (!vs->cbpeer.notifier.notify) {
  295. vs->cbpeer.name = "vnc";
  296. vs->cbpeer.notifier.notify = vnc_clipboard_notify;
  297. vs->cbpeer.request = vnc_clipboard_request;
  298. qemu_clipboard_peer_register(&vs->cbpeer);
  299. }
  300. }