VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-win.cpp@ 78160

Last change on this file since 78160 was 78160, checked in by vboxsync, 6 years ago

Shared Clipboard: Unified naming of host service source files; too many similarly named files in different otherwise, can be confusing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-win.cpp 78160 2019-04-17 13:43:22Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Win32 host.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
23#include <iprt/win/windows.h>
24
25#include <VBox/HostServices/VBoxClipboardSvc.h>
26#include <VBox/GuestHost/SharedClipboard-win.h>
27
28#include <iprt/alloc.h>
29#include <iprt/string.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/thread.h>
33#include <iprt/ldr.h>
34#include <process.h>
35
36#include "VBoxClipboard.h"
37
38/** Static window class name. */
39static char s_szClipWndClassName[] = VBOX_CLIPBOARD_WNDCLASS_NAME;
40
41/*********************************************************************************************************************************
42* Internal Functions *
43*********************************************************************************************************************************/
44static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pch);
45static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput);
46static bool IsWindowsHTML(const char *source);
47static int vboxClipboardSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx);
48
49struct _VBOXCLIPBOARDCONTEXT
50{
51 /** Handle for window message handling thread. */
52 RTTHREAD hThread;
53 /** Event which gets triggered if the host clipboard needs to render its data. */
54 HANDLE hRenderEvent;
55 /** Structure for keeping and communicating with client data (from the guest). */
56 PVBOXCLIPBOARDCLIENTDATA pClient;
57 /** Windows-specific context data. */
58 VBOXCLIPBOARDWINCTX Win;
59};
60
61/* Only one client is supported. There seems to be no need for more clients. */
62static VBOXCLIPBOARDCONTEXT g_ctx;
63
64
65#ifdef LOG_ENABLED
66static void vboxClipboardDump(const void *pv, size_t cb, uint32_t u32Format)
67{
68 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
69 {
70 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT:\n"));
71 if (pv && cb)
72 LogFunc(("%ls\n", pv));
73 else
74 LogFunc(("%p %zu\n", pv, cb));
75 }
76 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
77 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_BITMAP\n"));
78 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
79 {
80 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_HTML:\n"));
81 if (pv && cb)
82 {
83 LogFunc(("%s\n", pv));
84
85 //size_t cb = RTStrNLen(pv, );
86 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
87 RTStrCopy(pszBuf, cb + 1, (const char *)pv);
88 for (size_t off = 0; off < cb; ++off)
89 {
90 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
91 pszBuf[off] = ' ';
92 }
93
94 LogFunc(("%s\n", pszBuf));
95 RTMemFree(pszBuf);
96 }
97 else
98 LogFunc(("%p %zu\n", pv, cb));
99 }
100 else
101 LogFunc(("Invalid format %02X\n", u32Format));
102}
103#else /* !LOG_ENABLED */
104# define vboxClipboardDump(__pv, __cb, __format) do { NOREF(__pv); NOREF(__cb); NOREF(__format); } while (0)
105#endif /* !LOG_ENABLED */
106
107/** @todo Someone please explain the protocol wrt overflows... */
108static void vboxClipboardGetData (uint32_t u32Format, const void *pvSrc, uint32_t cbSrc,
109 void *pvDst, uint32_t cbDst, uint32_t *pcbActualDst)
110{
111 LogFlow(("vboxClipboardGetData cbSrc = %d, cbDst = %d\n", cbSrc, cbDst));
112
113 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
114 && IsWindowsHTML((const char *)pvSrc))
115 {
116 /** @todo r=bird: Why the double conversion? */
117 char *pszBuf = NULL;
118 uint32_t cbBuf = 0;
119 int rc = ConvertCFHtmlToMime((const char*)pvSrc, cbSrc, &pszBuf, &cbBuf);
120 if (RT_SUCCESS(rc))
121 {
122 *pcbActualDst = cbBuf;
123 if (cbBuf > cbDst)
124 {
125 /* Do not copy data. The dst buffer is not enough. */
126 RTMemFree(pszBuf);
127 return;
128 }
129 memcpy(pvDst, pszBuf, cbBuf);
130 RTMemFree(pszBuf);
131 }
132 else
133 *pcbActualDst = 0;
134 }
135 else
136 {
137 *pcbActualDst = cbSrc;
138
139 if (cbSrc > cbDst)
140 {
141 /* Do not copy data. The dst buffer is not enough. */
142 return;
143 }
144
145 memcpy(pvDst, pvSrc, cbSrc);
146 }
147
148 vboxClipboardDump(pvDst, cbSrc, u32Format);
149
150 return;
151}
152
153static int vboxClipboardReadDataFromClient (VBOXCLIPBOARDCONTEXT *pCtx, uint32_t u32Format)
154{
155 Assert(pCtx->pClient);
156 Assert(pCtx->hRenderEvent);
157 Assert(pCtx->pClient->data.pv == NULL && pCtx->pClient->data.cb == 0 && pCtx->pClient->data.u32Format == 0);
158
159 LogFlow(("vboxClipboardReadDataFromClient u32Format = %02X\n", u32Format));
160
161 ResetEvent (pCtx->hRenderEvent);
162
163 vboxSvcClipboardReportMsg (pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
164
165 DWORD ret = WaitForSingleObject(pCtx->hRenderEvent, INFINITE);
166 LogFlow(("vboxClipboardReadDataFromClient wait completed, ret 0x%08X, err %d\n",
167 ret, GetLastError())); NOREF(ret);
168
169 return VINF_SUCCESS;
170}
171
172static LRESULT CALLBACK vboxClipboardWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
173{
174 LRESULT rc = 0;
175
176 PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
177
178 switch (msg)
179 {
180 case WM_CLIPBOARDUPDATE:
181 {
182 Log(("WM_CLIPBOARDUPDATE\n"));
183
184 if (GetClipboardOwner() != hwnd)
185 {
186 /* Clipboard was updated by another application, retrieve formats and report back. */
187 int vboxrc = vboxClipboardSyncInternal(pCtx);
188 AssertRC(vboxrc);
189 }
190 } break;
191
192 case WM_CHANGECBCHAIN:
193 {
194 Log(("WM_CHANGECBCHAIN\n"));
195
196 if (VBoxClipboardWinIsNewAPI(&pCtx->Win.newAPI))
197 {
198 rc = DefWindowProc(hwnd, msg, wParam, lParam);
199 break;
200 }
201
202 HWND hwndRemoved = (HWND)wParam;
203 HWND hwndNext = (HWND)lParam;
204
205 if (hwndRemoved == pCtx->Win.hWndNextInChain)
206 {
207 /* The window that was next to our in the chain is being removed.
208 * Relink to the new next window.
209 */
210 pCtx->Win.hWndNextInChain = hwndNext;
211 }
212 else
213 {
214 if (pCtx->Win.hWndNextInChain)
215 {
216 /* Pass the message further. */
217 DWORD_PTR dwResult;
218 rc = SendMessageTimeout(pCtx->Win.hWndNextInChain, WM_CHANGECBCHAIN, wParam, lParam, 0,
219 VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
220 &dwResult);
221 if (!rc)
222 rc = (LRESULT)dwResult;
223 }
224 }
225 } break;
226
227 case WM_DRAWCLIPBOARD:
228 {
229 Log(("WM_DRAWCLIPBOARD\n"));
230
231 if (GetClipboardOwner() != hwnd)
232 {
233 /* Clipboard was updated by another application, retrieve formats and report back. */
234 int vboxrc = vboxClipboardSyncInternal(pCtx);
235 AssertRC(vboxrc);
236 }
237
238 if (pCtx->Win.hWndNextInChain)
239 {
240 Log(("WM_DRAWCLIPBOARD next %p\n", pCtx->Win.hWndNextInChain));
241 /* Pass the message to next windows in the clipboard chain. */
242 DWORD_PTR dwResult;
243 rc = SendMessageTimeout(pCtx->Win.hWndNextInChain, msg, wParam, lParam, 0, VBOX_CLIPBOARD_CBCHAIN_TIMEOUT_MS,
244 &dwResult);
245 if (!rc)
246 rc = dwResult;
247 }
248 } break;
249
250 case WM_TIMER:
251 {
252 if (VBoxClipboardWinIsNewAPI(&pCtx->Win.newAPI))
253 break;
254
255 HWND hViewer = GetClipboardViewer();
256
257 /* Re-register ourselves in the clipboard chain if our last ping
258 * timed out or there seems to be no valid chain. */
259 if (!hViewer || pCtx->Win.oldAPI.fCBChainPingInProcess)
260 {
261 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
262 VBoxClipboardWinAddToCBChain(&pCtx->Win);
263 }
264
265 /* Start a new ping by passing a dummy WM_CHANGECBCHAIN to be
266 * processed by ourselves to the chain. */
267 pCtx->Win.oldAPI.fCBChainPingInProcess = TRUE;
268
269 hViewer = GetClipboardViewer();
270 if (hViewer)
271 SendMessageCallback(hViewer, WM_CHANGECBCHAIN,
272 (WPARAM)pCtx->Win.hWndNextInChain, (LPARAM)pCtx->Win.hWndNextInChain,
273 VBoxClipboardWinChainPingProc, (ULONG_PTR)pCtx);
274 } break;
275
276 case WM_RENDERFORMAT:
277 {
278 /* Insert the requested clipboard format data into the clipboard. */
279 uint32_t u32Format = 0;
280
281 UINT format = (UINT)wParam;
282
283 Log(("WM_RENDERFORMAT %d\n", format));
284
285 switch (format)
286 {
287 case CF_UNICODETEXT:
288 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
289 break;
290
291 case CF_DIB:
292 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
293 break;
294
295 default:
296 if (format >= 0xC000)
297 {
298 TCHAR szFormatName[256];
299
300 int cActual = GetClipboardFormatName(format, szFormatName, sizeof(szFormatName)/sizeof (TCHAR));
301
302 if (cActual)
303 {
304 if (strcmp (szFormatName, "HTML Format") == 0)
305 {
306 u32Format |= VBOX_SHARED_CLIPBOARD_FMT_HTML;
307 }
308 }
309 }
310 break;
311 }
312
313 if (u32Format == 0 || pCtx->pClient == NULL)
314 {
315 /* Unsupported clipboard format is requested. */
316 Log(("WM_RENDERFORMAT unsupported format requested or client is not active.\n"));
317 EmptyClipboard ();
318 }
319 else
320 {
321 int vboxrc = vboxClipboardReadDataFromClient (pCtx, u32Format);
322
323 LogFunc(("vboxClipboardReadDataFromClient vboxrc = %d, pv %p, cb %d, u32Format %d\n",
324 vboxrc, pCtx->pClient->data.pv, pCtx->pClient->data.cb, pCtx->pClient->data.u32Format));
325
326 if ( RT_SUCCESS (vboxrc)
327 && pCtx->pClient->data.pv != NULL
328 && pCtx->pClient->data.cb > 0
329 && pCtx->pClient->data.u32Format == u32Format)
330 {
331 HANDLE hMem = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, pCtx->pClient->data.cb);
332
333 LogFunc(("hMem %p\n", hMem));
334
335 if (hMem)
336 {
337 void *pMem = GlobalLock (hMem);
338
339 LogFunc(("pMem %p, GlobalSize %d\n", pMem, GlobalSize (hMem)));
340
341 if (pMem)
342 {
343 Log(("WM_RENDERFORMAT setting data\n"));
344
345 if (pCtx->pClient->data.pv)
346 {
347 memcpy (pMem, pCtx->pClient->data.pv, pCtx->pClient->data.cb);
348
349 RTMemFree (pCtx->pClient->data.pv);
350 pCtx->pClient->data.pv = NULL;
351 }
352
353 pCtx->pClient->data.cb = 0;
354 pCtx->pClient->data.u32Format = 0;
355
356 /* The memory must be unlocked before inserting to the Clipboard. */
357 GlobalUnlock (hMem);
358
359 /* 'hMem' contains the host clipboard data.
360 * size is 'cb' and format is 'format'.
361 */
362 HANDLE hClip = SetClipboardData (format, hMem);
363
364 LogFunc(("vboxClipboardHostEvent hClip %p\n", hClip));
365
366 if (hClip)
367 {
368 /* The hMem ownership has gone to the system. Nothing to do. */
369 break;
370 }
371 }
372
373 GlobalFree (hMem);
374 }
375 }
376
377 RTMemFree (pCtx->pClient->data.pv);
378 pCtx->pClient->data.pv = NULL;
379 pCtx->pClient->data.cb = 0;
380 pCtx->pClient->data.u32Format = 0;
381
382 /* Something went wrong. */
383 VBoxClipboardWinClear();
384 }
385 } break;
386
387 case WM_RENDERALLFORMATS:
388 {
389 Log(("WM_RENDERALLFORMATS\n"));
390
391 /* Do nothing. The clipboard formats will be unavailable now, because the
392 * windows is to be destroyed and therefore the guest side becomes inactive.
393 */
394 int vboxrc = VBoxClipboardWinOpen(hwnd);
395 if (RT_SUCCESS(vboxrc))
396 {
397 VBoxClipboardWinClear();
398 VBoxClipboardWinClose();
399 }
400 else
401 {
402 LogFlow(("vboxClipboardWndProc: WM_RENDERALLFORMATS: error in open clipboard. hwnd: %x, rc: %Rrc\n", hwnd, vboxrc));
403 }
404 } break;
405
406 case VBOX_CLIPBOARD_WM_SET_FORMATS:
407 {
408 if (pCtx->pClient == NULL || pCtx->pClient->fMsgFormats)
409 {
410 /* Host has pending formats message. Ignore the guest announcement,
411 * because host clipboard has more priority.
412 */
413 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS ignored\n"));
414 break;
415 }
416
417 /* Announce available formats. Do not insert data, they will be inserted in WM_RENDER*. */
418 uint32_t u32Formats = (uint32_t)lParam;
419
420 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS: u32Formats=%02X\n", u32Formats));
421
422 int vboxrc = VBoxClipboardWinOpen(hwnd);
423 if (RT_SUCCESS(vboxrc))
424 {
425 VBoxClipboardWinClear();
426
427 Log(("VBOX_CLIPBOARD_WM_SET_FORMATS emptied clipboard\n"));
428
429 HANDLE hClip = NULL;
430
431 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
432 hClip = SetClipboardData(CF_UNICODETEXT, NULL);
433
434 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
435 hClip = SetClipboardData(CF_DIB, NULL);
436
437 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_HTML)
438 {
439 UINT format = RegisterClipboardFormat ("HTML Format");
440 if (format != 0)
441 {
442 hClip = SetClipboardData (format, NULL);
443 }
444 }
445
446 VBoxClipboardWinClose();
447
448 LogFunc(("VBOX_CLIPBOARD_WM_SET_FORMATS: hClip=%p, lastErr=%ld\n", hClip, GetLastError ()));
449 }
450 } break;
451
452 case WM_DESTROY:
453 {
454 /* MS recommends to remove from Clipboard chain in this callback. */
455 VBoxClipboardWinRemoveFromCBChain(&pCtx->Win);
456 if (pCtx->Win.oldAPI.timerRefresh)
457 {
458 Assert(pCtx->Win.hWnd);
459 KillTimer(pCtx->Win.hWnd, 0);
460 }
461 PostQuitMessage(0);
462 } break;
463
464 default:
465 {
466 Log(("WM_ %p\n", msg));
467 rc = DefWindowProc(hwnd, msg, wParam, lParam);
468 }
469 }
470
471 Log(("WM_ rc %d\n", rc));
472 return rc;
473}
474
475DECLCALLBACK(int) VBoxClipboardThread (RTTHREAD hThreadSelf, void *pvUser)
476{
477 RT_NOREF2(hThreadSelf, pvUser);
478 /* Create a window and make it a clipboard viewer. */
479 int rc = VINF_SUCCESS;
480
481 LogFlow(("VBoxClipboardThread\n"));
482
483 const PVBOXCLIPBOARDCONTEXT pCtx = &g_ctx;
484
485 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
486
487 /* Register the Window Class. */
488 WNDCLASS wc;
489 RT_ZERO(wc);
490
491 wc.style = CS_NOCLOSE;
492 wc.lpfnWndProc = vboxClipboardWndProc;
493 wc.hInstance = hInstance;
494 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
495 wc.lpszClassName = s_szClipWndClassName;
496
497 ATOM atomWindowClass = RegisterClass(&wc);
498
499 if (atomWindowClass == 0)
500 {
501 Log(("Failed to register window class\n"));
502 rc = VERR_NOT_SUPPORTED;
503 }
504 else
505 {
506 /* Create the window. */
507 pCtx->Win.hWnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
508 s_szClipWndClassName, s_szClipWndClassName,
509 WS_POPUPWINDOW,
510 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
511 if (pCtx->Win.hWnd == NULL)
512 {
513 Log(("Failed to create window\n"));
514 rc = VERR_NOT_SUPPORTED;
515 }
516 else
517 {
518 SetWindowPos(pCtx->Win.hWnd, HWND_TOPMOST, -200, -200, 0, 0,
519 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
520
521 VBoxClipboardWinAddToCBChain(&pCtx->Win);
522 if (!VBoxClipboardWinIsNewAPI(&pCtx->Win.newAPI))
523 pCtx->Win.oldAPI.timerRefresh = SetTimer(pCtx->Win.hWnd, 0, 10 * 1000, NULL);
524
525 MSG msg;
526 BOOL msgret = 0;
527 while ((msgret = GetMessage(&msg, NULL, 0, 0)) > 0)
528 {
529 TranslateMessage(&msg);
530 DispatchMessage(&msg);
531 }
532 /*
533 * Window procedure can return error,
534 * but this is exceptional situation
535 * that should be identified in testing
536 */
537 Assert(msgret >= 0);
538 Log(("VBoxClipboardThread Message loop finished. GetMessage returned %d, message id: %d \n", msgret, msg.message));
539 }
540 }
541
542 pCtx->Win.hWnd = NULL;
543
544 if (atomWindowClass != 0)
545 {
546 UnregisterClass (s_szClipWndClassName, hInstance);
547 atomWindowClass = 0;
548 }
549
550 return 0;
551}
552
553/**
554 * Synchronizes the host and the guest clipboard formats by sending all supported host clipboard
555 * formats to the guest.
556 *
557 * @returns VBox status code.
558 * @param pCtx Clipboard context to synchronize.
559 */
560static int vboxClipboardSyncInternal(PVBOXCLIPBOARDCONTEXT pCtx)
561{
562 uint32_t uFormats;
563 int rc = VBoxClipboardWinGetFormats(&pCtx->Win, &uFormats);
564 if (RT_SUCCESS(rc))
565 vboxSvcClipboardReportMsg(pCtx->pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS, uFormats);
566
567 return rc;
568}
569
570/*
571 * Public platform dependent functions.
572 */
573int vboxClipboardInit (void)
574{
575 int rc = VINF_SUCCESS;
576
577 RT_ZERO(g_ctx);
578
579 /* Check that new Clipboard API is available. */
580 VBoxClipboardWinCheckAndInitNewAPI(&g_ctx.Win.newAPI);
581
582 g_ctx.hRenderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
583
584 rc = RTThreadCreate (&g_ctx.hThread, VBoxClipboardThread, NULL, 65536,
585 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "SHCLIP");
586
587 if (RT_FAILURE (rc))
588 {
589 CloseHandle (g_ctx.hRenderEvent);
590 }
591
592 return rc;
593}
594
595void vboxClipboardDestroy (void)
596{
597 Log(("vboxClipboardDestroy\n"));
598
599 if (g_ctx.Win.hWnd)
600 {
601 PostMessage (g_ctx.Win.hWnd, WM_CLOSE, 0, 0);
602 }
603
604 CloseHandle (g_ctx.hRenderEvent);
605
606 /* Wait for the window thread to terminate. */
607 RTThreadWait (g_ctx.hThread, RT_INDEFINITE_WAIT, NULL);
608
609 g_ctx.hThread = NIL_RTTHREAD;
610}
611
612int vboxClipboardConnect (VBOXCLIPBOARDCLIENTDATA *pClient, bool fHeadless)
613{
614 NOREF(fHeadless);
615 Log(("vboxClipboardConnect\n"));
616
617 if (g_ctx.pClient != NULL)
618 {
619 /* One client only. */
620 return VERR_NOT_SUPPORTED;
621 }
622
623 pClient->pCtx = &g_ctx;
624
625 pClient->pCtx->pClient = pClient;
626
627 /* Sync the host clipboard content with the client. */
628 vboxClipboardSync (pClient);
629
630 return VINF_SUCCESS;
631}
632
633int vboxClipboardSync (VBOXCLIPBOARDCLIENTDATA *pClient)
634{
635 /* Sync the host clipboard content with the client. */
636 return vboxClipboardSyncInternal(pClient->pCtx);
637}
638
639void vboxClipboardDisconnect (VBOXCLIPBOARDCLIENTDATA *pClient)
640{
641 RT_NOREF1(pClient);
642 Log(("vboxClipboardDisconnect\n"));
643
644 g_ctx.pClient = NULL;
645}
646
647void vboxClipboardFormatAnnounce (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Formats)
648{
649 /*
650 * The guest announces formats. Forward to the window thread.
651 */
652 PostMessage (pClient->pCtx->Win.hWnd, WM_USER, 0, u32Formats);
653}
654
655int DumpHtml(const char *pszSrc, size_t cb)
656{
657 size_t cchIgnored = 0;
658 int rc = RTStrNLenEx(pszSrc, cb, &cchIgnored);
659 if (RT_SUCCESS(rc))
660 {
661 char *pszBuf = (char *)RTMemAllocZ(cb + 1);
662 if (pszBuf != NULL)
663 {
664 rc = RTStrCopy(pszBuf, cb + 1, (const char *)pszSrc);
665 if (RT_SUCCESS(rc))
666 {
667 for (size_t i = 0; i < cb; ++i)
668 if (pszBuf[i] == '\n' || pszBuf[i] == '\r')
669 pszBuf[i] = ' ';
670 }
671 else
672 Log(("Error in copying string.\n"));
673 Log(("Removed \\r\\n: %s\n", pszBuf));
674 RTMemFree(pszBuf);
675 }
676 else
677 {
678 rc = VERR_NO_MEMORY;
679 Log(("Not enough memory to allocate buffer.\n"));
680 }
681 }
682 return rc;
683}
684
685int vboxClipboardReadData (VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Format, void *pv, uint32_t cb, uint32_t *pcbActual)
686{
687 LogFlow(("vboxClipboardReadData: u32Format = %02X\n", u32Format));
688
689 HANDLE hClip = NULL;
690
691 /*
692 * The guest wants to read data in the given format.
693 */
694 int rc = VBoxClipboardWinOpen(pClient->pCtx->Win.hWnd);
695 if (RT_SUCCESS(rc))
696 {
697 LogFunc(("Clipboard opened.\n"));
698
699 if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
700 {
701 hClip = GetClipboardData (CF_DIB);
702
703 if (hClip != NULL)
704 {
705 LPVOID lp = GlobalLock (hClip);
706
707 if (lp != NULL)
708 {
709 LogFunc(("CF_DIB\n"));
710
711 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_BITMAP, lp, GlobalSize (hClip),
712 pv, cb, pcbActual);
713
714 GlobalUnlock(hClip);
715 }
716 else
717 {
718 hClip = NULL;
719 }
720 }
721 }
722 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
723 {
724 hClip = GetClipboardData(CF_UNICODETEXT);
725
726 if (hClip != NULL)
727 {
728 LPWSTR uniString = (LPWSTR)GlobalLock (hClip);
729
730 if (uniString != NULL)
731 {
732 LogFunc(("CF_UNICODETEXT\n"));
733
734 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT, uniString, (lstrlenW (uniString) + 1) * 2,
735 pv, cb, pcbActual);
736
737 GlobalUnlock(hClip);
738 }
739 else
740 {
741 hClip = NULL;
742 }
743 }
744 }
745 else if (u32Format & VBOX_SHARED_CLIPBOARD_FMT_HTML)
746 {
747 UINT format = RegisterClipboardFormat ("HTML Format");
748
749 if (format != 0)
750 {
751 hClip = GetClipboardData (format);
752
753 if (hClip != NULL)
754 {
755 LPVOID lp = GlobalLock (hClip);
756
757 if (lp != NULL)
758 {
759 LogFunc(("CF_HTML\n"));
760
761 vboxClipboardGetData (VBOX_SHARED_CLIPBOARD_FMT_HTML, lp, GlobalSize (hClip),
762 pv, cb, pcbActual);
763 LogRelFlowFunc(("Raw HTML clipboard data from host :"));
764 DumpHtml((char*)pv, cb);
765 GlobalUnlock(hClip);
766 }
767 else
768 {
769 hClip = NULL;
770 }
771 }
772 }
773 }
774
775 VBoxClipboardWinClose();
776 }
777 else
778 {
779 LogFunc(("vboxClipboardReadData: failed to open clipboard, rc: %Rrc\n", rc));
780 }
781
782 if (hClip == NULL)
783 {
784 /* Reply with empty data. */
785 vboxClipboardGetData (0, NULL, 0,
786 pv, cb, pcbActual);
787 }
788
789 return VINF_SUCCESS;
790}
791
792void vboxClipboardWriteData (VBOXCLIPBOARDCLIENTDATA *pClient, void *pv, uint32_t cb, uint32_t u32Format)
793{
794 LogFlow(("vboxClipboardWriteData\n"));
795
796 /*
797 * The guest returns data that was requested in the WM_RENDERFORMAT handler.
798 */
799 Assert(pClient->data.pv == NULL && pClient->data.cb == 0 && pClient->data.u32Format == 0);
800
801 vboxClipboardDump(pv, cb, u32Format);
802
803 if (cb > 0)
804 {
805 char *pszResult = NULL;
806
807 if ( u32Format == VBOX_SHARED_CLIPBOARD_FMT_HTML
808 && !IsWindowsHTML((const char*)pv))
809 {
810 /* check that this is not already CF_HTML */
811 uint32_t cbResult;
812 int rc = ConvertMimeToCFHTML((const char *)pv, cb, &pszResult, &cbResult);
813 if (RT_SUCCESS(rc))
814 {
815 if (pszResult != NULL && cbResult != 0)
816 {
817 pClient->data.pv = pszResult;
818 pClient->data.cb = cbResult;
819 pClient->data.u32Format = u32Format;
820 }
821 }
822 }
823 else
824 {
825 pClient->data.pv = RTMemDup(pv, cb);
826 if (pClient->data.pv)
827 {
828 pClient->data.cb = cb;
829 pClient->data.u32Format = u32Format;
830 }
831 }
832 }
833
834 SetEvent(pClient->pCtx->hRenderEvent);
835}
836
837
838/**
839 * Extracts field value from CF_HTML struct
840 *
841 * @returns VBox status code
842 * @param pszSrc source in CF_HTML format
843 * @param pszOption Name of CF_HTML field
844 * @param puValue Where to return extracted value of CF_HTML field
845 */
846static int GetHeaderValue(const char *pszSrc, const char *pszOption, uint32_t *puValue)
847{
848 int rc = VERR_INVALID_PARAMETER;
849
850 Assert(pszSrc);
851 Assert(pszOption);
852
853 const char *pszOptionValue = RTStrStr(pszSrc, pszOption);
854 if (pszOptionValue)
855 {
856 size_t cchOption = strlen(pszOption);
857 Assert(cchOption);
858
859 rc = RTStrToUInt32Ex(pszOptionValue + cchOption, NULL, 10, puValue);
860 }
861 return rc;
862}
863
864
865/**
866 * Check that the source string contains CF_HTML struct
867 *
868 * @param pszSource source string.
869 *
870 * @returns @c true if the @a pszSource string is in CF_HTML format
871 */
872static bool IsWindowsHTML(const char *pszSource)
873{
874 return RTStrStr(pszSource, "Version:") != NULL
875 && RTStrStr(pszSource, "StartHTML:") != NULL;
876}
877
878
879/*
880 * Converts clipboard data from CF_HTML format to mimie clipboard format
881 *
882 * Returns allocated buffer that contains html converted to text/html mime type
883 *
884 * @returns VBox status code.
885 * @param pszSource The input.
886 * @param cch The length of the input.
887 * @param ppszOutput Where to return the result. Free using RTMemFree.
888 * @param pcbOutput Where to the return length of the result (bytes/chars).
889 */
890static int ConvertCFHtmlToMime(const char *pszSource, const uint32_t cch, char **ppszOutput, uint32_t *pcbOutput)
891{
892 Assert(pszSource);
893 Assert(cch);
894 Assert(ppszOutput);
895 Assert(pcbOutput);
896
897 uint32_t offStart;
898 int rc = GetHeaderValue(pszSource, "StartFragment:", &offStart);
899 if (RT_SUCCESS(rc))
900 {
901 uint32_t offEnd;
902 rc = GetHeaderValue(pszSource, "EndFragment:", &offEnd);
903 if (RT_SUCCESS(rc))
904 {
905 if ( offStart > 0
906 && offEnd > 0
907 && offEnd > offStart
908 && offEnd <= cch)
909 {
910 uint32_t cchSubStr = offEnd - offStart;
911 char *pszResult = (char *)RTMemAlloc(cchSubStr + 1);
912 if (pszResult)
913 {
914 rc = RTStrCopyEx(pszResult, cchSubStr + 1, pszSource + offStart, cchSubStr);
915 if (RT_SUCCESS(rc))
916 {
917 *ppszOutput = pszResult;
918 *pcbOutput = (uint32_t)(cchSubStr + 1);
919 rc = VINF_SUCCESS;
920 }
921 else
922 {
923 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc\n", rc));
924 RTMemFree(pszResult);
925 }
926 }
927 else
928 {
929 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment.\n"));
930 rc = VERR_NO_MEMORY;
931 }
932 }
933 else
934 {
935 LogRelFlowFunc(("Error: CF_HTML out of bounds - offStart=%#x offEnd=%#x cch=%#x\n", offStart, offEnd, cch));
936 rc = VERR_INVALID_PARAMETER;
937 }
938 }
939 else
940 {
941 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected EndFragment. rc = %Rrc.\n", rc));
942 rc = VERR_INVALID_PARAMETER;
943 }
944 }
945 else
946 {
947 LogRelFlowFunc(("Error: Unknown CF_HTML format. Expected StartFragment. rc = %Rrc.\n", rc));
948 rc = VERR_INVALID_PARAMETER;
949 }
950
951 return rc;
952}
953
954
955
956/**
957 * Converts source UTF-8 MIME HTML clipboard data to UTF-8 CF_HTML format.
958 *
959 * This is just encapsulation work, slapping a header on the data.
960 *
961 * It allocates
962 *
963 * Calculations:
964 * Header length = format Length + (2*(10 - 5('%010d'))('digits')) - 2('%s') = format length + 8
965 * EndHtml = Header length + fragment length
966 * StartHtml = 105(constant)
967 * StartFragment = 141(constant) may vary if the header html content will be extended
968 * EndFragment = Header length + fragment length - 38(ending length)
969 *
970 * @param pszSource Source buffer that contains utf-16 string in mime html format
971 * @param cb Size of source buffer in bytes
972 * @param ppszOutput Where to return the allocated output buffer to put converted UTF-8
973 * CF_HTML clipboard data. This function allocates memory for this.
974 * @param pcbOutput Where to return the size of allocated result buffer in bytes/chars, including zero terminator
975 *
976 * @note output buffer should be free using RTMemFree()
977 * @note Everything inside of fragment can be UTF8. Windows allows it. Everything in header should be Latin1.
978 */
979static int ConvertMimeToCFHTML(const char *pszSource, size_t cb, char **ppszOutput, uint32_t *pcbOutput)
980{
981 Assert(ppszOutput);
982 Assert(pcbOutput);
983 Assert(pszSource);
984 Assert(cb);
985
986 /* construct CF_HTML formatted string */
987 char *pszResult = NULL;
988 size_t cchFragment;
989 int rc = RTStrNLenEx(pszSource, cb, &cchFragment);
990 if (!RT_SUCCESS(rc))
991 {
992 LogRelFlowFunc(("Error: invalid source fragment. rc = %Rrc.\n"));
993 return VERR_INVALID_PARAMETER;
994 }
995
996 /*
997 @StartHtml - pos before <html>
998 @EndHtml - whole size of text excluding ending zero char
999 @StartFragment - pos after <!--StartFragment-->
1000 @EndFragment - pos before <!--EndFragment-->
1001 @note: all values includes CR\LF inserted into text
1002 Calculations:
1003 Header length = format Length + (3*6('digits')) - 2('%s') = format length + 16 (control value - 183)
1004 EndHtml = Header length + fragment length
1005 StartHtml = 105(constant)
1006 StartFragment = 143(constant)
1007 EndFragment = Header length + fragment length - 40(ending length)
1008 */
1009 static const char s_szFormatSample[] =
1010 /* 0: */ "Version:1.0\r\n"
1011 /* 13: */ "StartHTML:000000101\r\n"
1012 /* 34: */ "EndHTML:%0000009u\r\n" // END HTML = Header length + fragment length
1013 /* 53: */ "StartFragment:000000137\r\n"
1014 /* 78: */ "EndFragment:%0000009u\r\n"
1015 /* 101: */ "<html>\r\n"
1016 /* 109: */ "<body>\r\n"
1017 /* 117: */ "<!--StartFragment-->"
1018 /* 137: */ "%s"
1019 /* 137+2: */ "<!--EndFragment-->\r\n"
1020 /* 157+2: */ "</body>\r\n"
1021 /* 166+2: */ "</html>\r\n";
1022 /* 175+2: */
1023 AssertCompile(sizeof(s_szFormatSample) == 175+2+1);
1024
1025 /* calculate parameters of CF_HTML header */
1026 size_t cchHeader = sizeof(s_szFormatSample) - 1;
1027 size_t offEndHtml = cchHeader + cchFragment;
1028 size_t offEndFragment = cchHeader + cchFragment - 38; /* 175-137 = 38 */
1029 pszResult = (char *)RTMemAlloc(offEndHtml + 1);
1030 if (pszResult == NULL)
1031 {
1032 LogRelFlowFunc(("Error: Cannot allocate memory for result buffer. rc = %Rrc.\n"));
1033 return VERR_NO_MEMORY;
1034 }
1035
1036 /* format result CF_HTML string */
1037 size_t cchFormatted = RTStrPrintf(pszResult, offEndHtml + 1,
1038 s_szFormatSample, offEndHtml, offEndFragment, pszSource);
1039 Assert(offEndHtml == cchFormatted); NOREF(cchFormatted);
1040
1041#ifdef VBOX_STRICT
1042 /* Control calculations. check consistency.*/
1043 static const char s_szStartFragment[] = "<!--StartFragment-->";
1044 static const char s_szEndFragment[] = "<!--EndFragment-->";
1045
1046 /* check 'StartFragment:' value */
1047 const char *pszRealStartFragment = RTStrStr(pszResult, s_szStartFragment);
1048 Assert(&pszRealStartFragment[sizeof(s_szStartFragment) - 1] - pszResult == 137);
1049
1050 /* check 'EndFragment:' value */
1051 const char *pszRealEndFragment = RTStrStr(pszResult, s_szEndFragment);
1052 Assert((size_t)(pszRealEndFragment - pszResult) == offEndFragment);
1053#endif
1054
1055 *ppszOutput = pszResult;
1056 *pcbOutput = (uint32_t)cchFormatted + 1;
1057 Assert(*pcbOutput == cchFormatted + 1);
1058
1059 return VINF_SUCCESS;
1060}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette