VirtualBox

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

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

Shared Clipboard: Coding style.

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