vnc-clipboard.c 9.5 KB

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