VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp@ 78544

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.8 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 78501 2019-05-14 11:36:25Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Host service entry points.
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/** @page pg_hostclip The Shared Clipboard Host Service
20 *
21 * The shared clipboard host service provides a proxy between the host's
22 * clipboard and a similar proxy running on a guest. The service is split
23 * into a platform-independent core and platform-specific backends. The
24 * service defines two communication protocols - one to communicate with the
25 * clipboard service running on the guest, and one to communicate with the
26 * backend. These will be described in a very skeletal fashion here.
27 *
28 * @section sec_hostclip_guest_proto The guest communication protocol
29 *
30 * The guest clipboard service communicates with the host service via HGCM
31 * (the host service runs as an HGCM service). The guest clipboard must
32 * connect to the host service before all else (Windows hosts currently only
33 * support one simultaneous connection). Once it has connected, it can send
34 * HGCM messages to the host services, some of which will receive replies from
35 * the host. The host can only reply to a guest message, it cannot initiate
36 * any communication. The guest can in theory send any number of messages in
37 * parallel (see the descriptions of the messages for the practice), and the
38 * host will receive these in sequence, and may reply to them at once
39 * (releasing the caller in the guest) or defer the reply until later.
40 *
41 * There are currently four messages defined. The first is
42 * VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG, which waits for a message from the
43 * host. Host messages currently defined are
44 * VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT (unused),
45 * VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA (request that the guest send the
46 * contents of its clipboard to the host) and
47 * VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS (to notify the guest that new
48 * clipboard data is available). If a host message is sent while the guest is
49 * not waiting, it will be queued until the guest requests it. At most one
50 * host message of each type will be kept in the queue. The host code only
51 * supports a single simultaneous VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG call
52 * from the guest.
53 *
54 * The second guest message is VBOX_SHARED_CLIPBOARD_FN_FORMATS, which tells
55 * the host that the guest has new clipboard data available. The third is
56 * VBOX_SHARED_CLIPBOARD_FN_READ_DATA, which asks the host to send its
57 * clipboard data and waits until it arrives. The host supports at most one
58 * simultaneous VBOX_SHARED_CLIPBOARD_FN_READ_DATA call from the guest - if a
59 * second call is made before the first has returned, the first will be
60 * aborted.
61 *
62 * The last guest message is VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA, which is
63 * used to send the contents of the guest clipboard to the host. This call
64 * should be used after the host has requested data from the guest.
65 *
66 * @section sec_hostclip_backend_proto The communication protocol with the
67 * platform-specific backend
68 *
69 * This section may be written in the future :)
70 */
71
72
73/*********************************************************************************************************************************
74* Header Files *
75*********************************************************************************************************************************/
76#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
77#include <VBox/HostServices/VBoxClipboardSvc.h>
78#include <VBox/HostServices/VBoxClipboardExt.h>
79
80#include <iprt/alloc.h>
81#include <iprt/string.h>
82#include <iprt/assert.h>
83#include <iprt/critsect.h>
84#include <VBox/err.h>
85#include <VBox/vmm/ssm.h>
86
87#include "VBoxClipboard.h"
88#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
89# include "VBoxSharedClipboardSvc-uri.h"
90#endif
91
92
93/*********************************************************************************************************************************
94* Global Variables *
95*********************************************************************************************************************************/
96static PVBOXHGCMSVCHELPERS g_pHelpers;
97
98static RTCRITSECT critsect;
99static uint32_t g_u32Mode;
100
101static PFNHGCMSVCEXT g_pfnExtension;
102static void *g_pvExtension;
103
104static VBOXCLIPBOARDCLIENTDATA *g_pClient;
105
106/* Serialization of data reading and format announcements from the RDP client. */
107static bool g_fReadingData = false;
108static bool g_fDelayedAnnouncement = false;
109static uint32_t g_u32DelayedFormats = 0;
110
111/** Is the clipboard running in headless mode? */
112static bool g_fHeadless = false;
113
114
115static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
116{
117 pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
118 pParm->u.uint32 = u32;
119}
120
121static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
122{
123 if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
124 {
125 *pu32 = pParm->u.uint32;
126 return VINF_SUCCESS;
127 }
128
129 return VERR_INVALID_PARAMETER;
130}
131
132#if 0
133static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
134{
135 pParm->type = VBOX_HGCM_SVC_PARM_PTR;
136 pParm->u.pointer.size = cb;
137 pParm->u.pointer.addr = pv;
138}
139#endif
140
141static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
142{
143 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
144 {
145 *ppv = pParm->u.pointer.addr;
146 *pcb = pParm->u.pointer.size;
147 return VINF_SUCCESS;
148 }
149
150 return VERR_INVALID_PARAMETER;
151}
152
153
154static uint32_t vboxSvcClipboardMode(void)
155{
156 return g_u32Mode;
157}
158
159#ifdef UNIT_TEST
160/** Testing interface, getter for clipboard mode */
161uint32_t TestClipSvcGetMode(void)
162{
163 return vboxSvcClipboardMode();
164}
165#endif
166
167/** Getter for headless setting. Also needed by testcase. */
168bool VBoxSvcClipboardGetHeadless(void)
169{
170 return g_fHeadless;
171}
172
173static void vboxSvcClipboardModeSet(uint32_t u32Mode)
174{
175 switch (u32Mode)
176 {
177 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
178 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
179 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
180 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
181 g_u32Mode = u32Mode;
182 break;
183
184 default:
185 g_u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
186 }
187}
188
189bool VBoxSvcClipboardLock(void)
190{
191 return RT_SUCCESS(RTCritSectEnter(&critsect));
192}
193
194void VBoxSvcClipboardUnlock(void)
195{
196 RTCritSectLeave(&critsect);
197}
198
199/* Set the HGCM parameters according to pending messages.
200 * Executed under the clipboard lock.
201 */
202static bool vboxSvcClipboardReturnMsg(VBOXCLIPBOARDCLIENTDATA *pClient, VBOXHGCMSVCPARM paParms[])
203{
204 /* Message priority is taken into account. */
205 if (pClient->fHostMsgQuit)
206 {
207 LogFlowFunc(("vboxSvcClipboardReturnMsg: Quit\n"));
208 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
209 VBoxHGCMParmUInt32Set(&paParms[1], 0);
210 pClient->fHostMsgQuit = false;
211 }
212 else if (pClient->fHostMsgReadData)
213 {
214 uint32_t fFormat = 0;
215
216 LogFlowFunc(("vboxSvcClipboardReturnMsg: ReadData %02X\n", pClient->u32RequestedFormat));
217 if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
218 fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
219 else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
220 fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
221 else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
222 fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
223#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
224 else if (pClient->u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
225 fFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
226#endif
227 else
228 {
229 LogRel2(("Clipboard: Unsupported format from guest (0x%x), skipping\n", fFormat));
230 pClient->u32RequestedFormat = 0;
231 }
232 pClient->u32RequestedFormat &= ~fFormat;
233 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
234 VBoxHGCMParmUInt32Set(&paParms[1], fFormat);
235 if (pClient->u32RequestedFormat == 0)
236 pClient->fHostMsgReadData = false;
237 }
238 else if (pClient->fHostMsgFormats)
239 {
240 LogFlowFunc(("vboxSvcClipboardReturnMsg: Formats %02X\n", pClient->u32AvailableFormats));
241 VBoxHGCMParmUInt32Set(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS);
242 VBoxHGCMParmUInt32Set(&paParms[1], pClient->u32AvailableFormats);
243 pClient->fHostMsgFormats = false;
244 }
245 else
246 {
247 /* No pending messages. */
248 LogFlowFunc(("vboxSvcClipboardReturnMsg: no message\n"));
249 return false;
250 }
251
252 /* Message information assigned. */
253 return true;
254}
255
256void vboxSvcClipboardReportMsg(VBOXCLIPBOARDCLIENTDATA *pClient, uint32_t u32Msg, uint32_t u32Formats)
257{
258 AssertPtrReturnVoid(pClient);
259
260 if (VBoxSvcClipboardLock())
261 {
262 switch (u32Msg)
263 {
264 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
265 {
266 LogFlowFunc(("Quit\n"));
267 pClient->fHostMsgQuit = true;
268 } break;
269 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
270 {
271 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
272 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
273 {
274 /* Skip the message. */
275 break;
276 }
277
278 LogFlowFunc(("ReadData %02X\n", u32Formats));
279 pClient->u32RequestedFormat = u32Formats;
280 pClient->fHostMsgReadData = true;
281 } break;
282 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
283 {
284 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
285 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
286 {
287 /* Skip the message. */
288 break;
289 }
290
291 LogFlowFunc(("Formats %02X\n", u32Formats));
292 pClient->u32AvailableFormats = u32Formats;
293 pClient->fHostMsgFormats = true;
294 } break;
295 default:
296 {
297 /* Invalid message. */
298 LogFlowFunc(("invalid message %d\n", u32Msg));
299 } break;
300 }
301
302 if (pClient->fAsync)
303 {
304 /* The client waits for a response. */
305 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClient, pClient->async.paParms);
306
307 /* Make a copy of the handle. */
308 VBOXHGCMCALLHANDLE callHandle = pClient->async.callHandle;
309
310 if (fMessageReturned)
311 {
312 /* There is a response. */
313 pClient->fAsync = false;
314 }
315
316 VBoxSvcClipboardUnlock();
317
318 if (fMessageReturned)
319 {
320 LogFlowFunc(("CallComplete\n"));
321 g_pHelpers->pfnCallComplete(callHandle, VINF_SUCCESS);
322 }
323 }
324 else
325 {
326 VBoxSvcClipboardUnlock();
327 }
328 }
329}
330
331static int svcInit(void)
332{
333 int rc = RTCritSectInit(&critsect);
334
335 if (RT_SUCCESS(rc))
336 {
337 vboxSvcClipboardModeSet(VBOX_SHARED_CLIPBOARD_MODE_OFF);
338
339 rc = vboxClipboardInit();
340
341 /* Clean up on failure, because 'svnUnload' will not be called
342 * if the 'svcInit' returns an error.
343 */
344 if (RT_FAILURE(rc))
345 {
346 RTCritSectDelete(&critsect);
347 }
348 }
349
350 return rc;
351}
352
353static DECLCALLBACK(int) svcUnload(void *)
354{
355 vboxClipboardDestroy();
356 RTCritSectDelete(&critsect);
357 return VINF_SUCCESS;
358}
359
360/**
361 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
362 * to the guest side.
363 */
364static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
365{
366 RT_NOREF(u32ClientID);
367
368 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
369
370 LogFunc(("u32ClientID = %d\n", u32ClientID));
371
372 vboxSvcClipboardReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0);
373
374 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
375
376 vboxClipboardDisconnect(pClient);
377
378 memset(pClient, 0, sizeof(*pClient));
379
380 g_pClient = NULL;
381
382 return VINF_SUCCESS;
383}
384
385static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
386{
387 RT_NOREF(fRequestor, fRestoring);
388 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
389
390 int rc = VINF_SUCCESS;
391
392 /* If there is already a client connected then we want to release it first. */
393 if (g_pClient != NULL)
394 {
395 uint32_t u32OldClientID = g_pClient->u32ClientID;
396
397 svcDisconnect(NULL, u32OldClientID, g_pClient);
398 /* And free the resources in the hgcm subsystem. */
399 g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, u32OldClientID);
400 }
401
402 /* Register the client. */
403 memset(pClient, 0, sizeof(*pClient));
404
405 pClient->u32ClientID = u32ClientID;
406
407 rc = vboxClipboardConnect(pClient, VBoxSvcClipboardGetHeadless());
408
409 if (RT_SUCCESS(rc))
410 {
411 g_pClient = pClient;
412 }
413
414 LogFlowFuncLeaveRC(rc);
415 return rc;
416}
417
418static DECLCALLBACK(void) svcCall(void *,
419 VBOXHGCMCALLHANDLE callHandle,
420 uint32_t u32ClientID,
421 void *pvClient,
422 uint32_t u32Function,
423 uint32_t cParms,
424 VBOXHGCMSVCPARM paParms[],
425 uint64_t tsArrival)
426{
427 RT_NOREF(u32ClientID, tsArrival);
428 int rc = VINF_SUCCESS;
429
430 LogFunc(("u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
431 u32ClientID, u32Function, cParms, paParms));
432
433 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
434
435 bool fAsynchronousProcessing = false;
436
437#ifdef DEBUG
438 uint32_t i;
439
440 for (i = 0; i < cParms; i++)
441 {
442 /** @todo parameters other than 32 bit */
443 LogFunc((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
444 }
445#endif
446
447 switch (u32Function)
448 {
449 case VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG:
450 {
451 /* The quest requests a host message. */
452 LogFunc(("VBOX_SHARED_CLIPBOARD_FN_GET_HOST_MSG\n"));
453
454 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
455 {
456 rc = VERR_INVALID_PARAMETER;
457 }
458 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
459 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
460 )
461 {
462 rc = VERR_INVALID_PARAMETER;
463 }
464 else
465 {
466 /* Atomically verify the client's state. */
467 if (VBoxSvcClipboardLock())
468 {
469 bool fMessageReturned = vboxSvcClipboardReturnMsg (pClient, paParms);
470
471 if (fMessageReturned)
472 {
473 /* Just return to the caller. */
474 pClient->fAsync = false;
475 }
476 else
477 {
478 /* No event available at the time. Process asynchronously. */
479 fAsynchronousProcessing = true;
480
481 pClient->fAsync = true;
482 pClient->async.callHandle = callHandle;
483 pClient->async.paParms = paParms;
484
485 LogFunc(("async.\n"));
486 }
487
488 VBoxSvcClipboardUnlock();
489 }
490 else
491 {
492 rc = VERR_NOT_SUPPORTED;
493 }
494 }
495 } break;
496
497 case VBOX_SHARED_CLIPBOARD_FN_REPORT_FORMATS:
498 {
499 /* The guest reports that some formats are available. */
500 LogFunc(("VBOX_SHARED_CLIPBOARD_FN_FORMATS\n"));
501
502 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_FORMATS)
503 {
504 rc = VERR_INVALID_PARAMETER;
505 }
506 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* formats */
507 )
508 {
509 rc = VERR_INVALID_PARAMETER;
510 }
511 else
512 {
513 uint32_t u32Formats;
514
515 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Formats);
516
517 if (RT_SUCCESS (rc))
518 {
519 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
520 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
521 {
522 rc = VERR_NOT_SUPPORTED;
523 break;
524 }
525
526 if (g_pfnExtension)
527 {
528 VBOXCLIPBOARDEXTPARMS parms;
529
530 parms.u32Format = u32Formats;
531
532 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
533 }
534 else
535 {
536 vboxClipboardFormatAnnounce (pClient, u32Formats);
537 }
538 }
539 }
540 } break;
541
542 case VBOX_SHARED_CLIPBOARD_FN_READ_DATA:
543 {
544 /* The guest wants to read data in the given format. */
545 LogFunc(("VBOX_SHARED_CLIPBOARD_FN_READ_DATA\n"));
546
547 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
548 {
549 rc = VERR_INVALID_PARAMETER;
550 }
551 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
552 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
553 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
554 )
555 {
556 rc = VERR_INVALID_PARAMETER;
557 }
558 else
559 {
560 uint32_t u32Format;
561 void *pv;
562 uint32_t cb;
563
564 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
565
566 if (RT_SUCCESS (rc))
567 {
568 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
569
570 if (RT_SUCCESS (rc))
571 {
572 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
573 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
574 {
575 rc = VERR_NOT_SUPPORTED;
576 break;
577 }
578
579 uint32_t cbActual = 0;
580
581 if (g_pfnExtension)
582 {
583 VBOXCLIPBOARDEXTPARMS parms;
584
585 parms.u32Format = u32Format;
586 parms.u.pvData = pv;
587 parms.cbData = cb;
588
589 g_fReadingData = true;
590 rc = g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
591 LogFlowFunc(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
592 if (g_fDelayedAnnouncement)
593 {
594 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, g_u32DelayedFormats);
595 g_fDelayedAnnouncement = false;
596 g_u32DelayedFormats = 0;
597 }
598 g_fReadingData = false;
599
600 if (RT_SUCCESS (rc))
601 {
602 cbActual = parms.cbData;
603 }
604 }
605 else
606 {
607 /* Release any other pending read, as we only
608 * support one pending read at one time. */
609 vboxSvcClipboardCompleteReadData(pClient, VERR_NO_DATA, 0);
610 rc = vboxClipboardReadData (pClient, u32Format, pv, cb, &cbActual);
611 }
612
613 /* Remember our read request until it is completed.
614 * See the protocol description above for more
615 * information. */
616 if (rc == VINF_HGCM_ASYNC_EXECUTE)
617 {
618 if (VBoxSvcClipboardLock())
619 {
620 pClient->asyncRead.callHandle = callHandle;
621 pClient->asyncRead.paParms = paParms;
622 pClient->fReadPending = true;
623 fAsynchronousProcessing = true;
624 VBoxSvcClipboardUnlock();
625 }
626 else
627 rc = VERR_NOT_SUPPORTED;
628 }
629 else if (RT_SUCCESS (rc))
630 {
631 VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
632 }
633 }
634 }
635 }
636 } break;
637
638 case VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA:
639 {
640 /* The guest writes the requested data. */
641 LogFunc(("VBOX_SHARED_CLIPBOARD_FN_WRITE_DATA\n"));
642
643 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
644 {
645 rc = VERR_INVALID_PARAMETER;
646 }
647 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
648 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
649 )
650 {
651 rc = VERR_INVALID_PARAMETER;
652 }
653 else
654 {
655 void *pv;
656 uint32_t cb;
657 uint32_t u32Format;
658
659 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Format);
660
661 if (RT_SUCCESS (rc))
662 {
663 rc = VBoxHGCMParmPtrGet (&paParms[1], &pv, &cb);
664
665 if (RT_SUCCESS (rc))
666 {
667 if ( vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
668 && vboxSvcClipboardMode () != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
669 {
670 rc = VERR_NOT_SUPPORTED;
671 break;
672 }
673
674 if (g_pfnExtension)
675 {
676 VBOXCLIPBOARDEXTPARMS parms;
677
678 parms.u32Format = u32Format;
679 parms.u.pvData = pv;
680 parms.cbData = cb;
681
682 g_pfnExtension (g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
683 }
684 else
685 {
686 vboxClipboardWriteData (pClient, pv, cb, u32Format);
687 }
688 }
689 }
690 }
691 } break;
692
693 default:
694 {
695#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
696 rc = vboxClipboardSvcURIHandler(u32ClientID, pvClient, u32Function, cParms, paParms, tsArrival,
697 &fAsynchronousProcessing);
698#else
699 rc = VERR_NOT_IMPLEMENTED;
700#endif
701 }
702 }
703
704 LogFlowFunc(("rc = %Rrc\n", rc));
705
706 if (!fAsynchronousProcessing)
707 {
708 g_pHelpers->pfnCallComplete(callHandle, rc);
709 }
710}
711
712/** If the client in the guest is waiting for a read operation to complete
713 * then complete it, otherwise return. See the protocol description in the
714 * shared clipboard module description. */
715void vboxSvcClipboardCompleteReadData(VBOXCLIPBOARDCLIENTDATA *pClient, int rc, uint32_t cbActual)
716{
717 VBOXHGCMCALLHANDLE callHandle = NULL;
718 VBOXHGCMSVCPARM *paParms = NULL;
719 bool fReadPending = false;
720 if (VBoxSvcClipboardLock()) /* if not can we do anything useful? */
721 {
722 callHandle = pClient->asyncRead.callHandle;
723 paParms = pClient->asyncRead.paParms;
724 fReadPending = pClient->fReadPending;
725 pClient->fReadPending = false;
726 VBoxSvcClipboardUnlock();
727 }
728 if (fReadPending)
729 {
730 VBoxHGCMParmUInt32Set(&paParms[2], cbActual);
731 g_pHelpers->pfnCallComplete(callHandle, rc);
732 }
733}
734
735/*
736 * We differentiate between a function handler for the guest and one for the host.
737 */
738static DECLCALLBACK(int) svcHostCall(void *,
739 uint32_t u32Function,
740 uint32_t cParms,
741 VBOXHGCMSVCPARM paParms[])
742{
743 int rc = VINF_SUCCESS;
744
745 LogFunc(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
746 u32Function, cParms, paParms));
747
748 switch (u32Function)
749 {
750 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
751 {
752 LogFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
753
754 if (cParms != 1)
755 {
756 rc = VERR_INVALID_PARAMETER;
757 }
758 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
759 )
760 {
761 rc = VERR_INVALID_PARAMETER;
762 }
763 else
764 {
765 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
766
767 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Mode);
768
769 /* The setter takes care of invalid values. */
770 vboxSvcClipboardModeSet (u32Mode);
771 }
772 } break;
773
774 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
775 {
776 uint32_t u32Headless = g_fHeadless;
777
778 rc = VERR_INVALID_PARAMETER;
779 if (cParms != 1)
780 break;
781 rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Headless);
782 if (RT_SUCCESS(rc))
783 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
784 (unsigned) u32Headless));
785 g_fHeadless = RT_BOOL(u32Headless);
786 } break;
787
788 default:
789 {
790#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
791 rc = vboxClipboardSvcURIHostHandler(u32Function, cParms, paParms);
792#endif
793 } break;
794 }
795
796 LogFlowFunc(("svcHostCall: rc = %Rrc\n", rc));
797 return rc;
798}
799
800#ifndef UNIT_TEST
801/**
802 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
803 */
804static SSMFIELD const g_aClipboardClientDataFields[] =
805{
806 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32ClientID), /* for validation purposes */
807 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fHostMsgQuit),
808 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fHostMsgReadData),
809 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, fHostMsgFormats),
810 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTDATA, u32RequestedFormat),
811 SSMFIELD_ENTRY_TERM()
812};
813#endif
814
815static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
816{
817 RT_NOREF(u32ClientID);
818
819#ifndef UNIT_TEST
820 /*
821 * When the state will be restored, pending requests will be reissued
822 * by VMMDev. The service therefore must save state as if there were no
823 * pending request.
824 * Pending requests, if any, will be completed in svcDisconnect.
825 */
826 LogFunc(("u32ClientID = %d\n", u32ClientID));
827
828 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
829
830 /* This field used to be the length. We're using it as a version field
831 with the high bit set. */
832 SSMR3PutU32(pSSM, UINT32_C(0x80000002));
833 int rc = SSMR3PutStructEx(pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
834 AssertRCReturn(rc, rc);
835
836#else /* UNIT_TEST */
837 RT_NOREF3(u32ClientID, pvClient, pSSM);
838#endif /* UNIT_TEST */
839 return VINF_SUCCESS;
840}
841
842/**
843 * This structure corresponds to the original layout of the
844 * VBOXCLIPBOARDCLIENTDATA structure. As the structure was saved as a whole
845 * when saving state, we need to remember it forever in order to preserve
846 * compatibility.
847 *
848 * (Starting with 3.1 this is no longer used.)
849 *
850 * @remarks Putting this outside svcLoadState to avoid visibility warning caused
851 * by -Wattributes.
852 */
853typedef struct CLIPSAVEDSTATEDATA
854{
855 struct CLIPSAVEDSTATEDATA *pNext;
856 struct CLIPSAVEDSTATEDATA *pPrev;
857
858 VBOXCLIPBOARDCONTEXT *pCtx;
859
860 uint32_t u32ClientID;
861
862 bool fAsync: 1; /* Guest is waiting for a message. */
863
864 bool fHostMsgQuit: 1;
865 bool fHostMsgReadData: 1;
866 bool fHostMsgFormats: 1;
867
868 struct
869 {
870 VBOXHGCMCALLHANDLE callHandle;
871 VBOXHGCMSVCPARM *paParms;
872 } async;
873
874 struct
875 {
876 void *pv;
877 uint32_t cb;
878 uint32_t u32Format;
879 } data;
880
881 uint32_t u32AvailableFormats;
882 uint32_t u32RequestedFormat;
883
884} CLIPSAVEDSTATEDATA;
885
886static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
887{
888#ifndef UNIT_TEST
889 RT_NOREF(u32ClientID, uVersion);
890
891 LogFunc(("u32ClientID = %d\n", u32ClientID));
892
893 VBOXCLIPBOARDCLIENTDATA *pClient = (VBOXCLIPBOARDCLIENTDATA *)pvClient;
894
895 /* Existing client can not be in async state yet. */
896 Assert(!pClient->fAsync);
897
898 /* Save the client ID for data validation. */
899 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
900 uint32_t const u32ClientIDOld = pClient->u32ClientID;
901
902 /* Restore the client data. */
903 uint32_t lenOrVer;
904 int rc = SSMR3GetU32(pSSM, &lenOrVer);
905 AssertRCReturn(rc, rc);
906 if (lenOrVer == UINT32_C(0x80000002))
907 {
908 rc = SSMR3GetStructEx(pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
909 AssertRCReturn(rc, rc);
910 }
911 else if (lenOrVer == (SSMR3HandleHostBits(pSSM) == 64 ? 72U : 48U))
912 {
913 /**
914 * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
915 */
916 static SSMFIELD const s_aClipSavedStateDataFields30[] =
917 {
918 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
919 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
920 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
921 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
922 SSMFIELD_ENTRY_CUSTOM(fMsgQuit + fMsgReadData + fMsgFormats, RT_UOFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
923 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
924 SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
925 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
926 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
927 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
928 SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
929 SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
930 SSMFIELD_ENTRY_TERM()
931 };
932
933 CLIPSAVEDSTATEDATA savedState;
934 RT_ZERO(savedState);
935 rc = SSMR3GetStructEx(pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
936 &s_aClipSavedStateDataFields30[0], NULL);
937 AssertRCReturn(rc, rc);
938
939 pClient->fHostMsgQuit = savedState.fHostMsgQuit;
940 pClient->fHostMsgReadData = savedState.fHostMsgReadData;
941 pClient->fHostMsgFormats = savedState.fHostMsgFormats;
942 pClient->u32RequestedFormat = savedState.u32RequestedFormat;
943 }
944 else
945 {
946 LogFunc(("Client data size mismatch: got %#x\n", lenOrVer));
947 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
948 }
949
950 /* Verify the client ID. */
951 if (pClient->u32ClientID != u32ClientIDOld)
952 {
953 LogFunc(("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
954 pClient->u32ClientID = u32ClientIDOld;
955 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
956 }
957
958 /* Actual host data are to be reported to guest (SYNC). */
959 vboxClipboardSync(pClient);
960
961#else /* UNIT_TEST*/
962 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
963#endif /* UNIT_TEST */
964 return VINF_SUCCESS;
965}
966
967static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
968{
969 RT_NOREF2(pvData, cbData);
970 if (g_pClient != NULL)
971 {
972 switch (u32Function)
973 {
974 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
975 {
976 LogFlowFunc(("ANNOUNCE: g_fReadingData = %d\n", g_fReadingData));
977 if (g_fReadingData)
978 {
979 g_fDelayedAnnouncement = true;
980 g_u32DelayedFormats = u32Format;
981 }
982 else
983 {
984 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, u32Format);
985 }
986 } break;
987
988 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
989 {
990 vboxSvcClipboardReportMsg (g_pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
991 } break;
992
993 default:
994 return VERR_NOT_SUPPORTED;
995 }
996 }
997
998 return VINF_SUCCESS;
999}
1000
1001static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
1002{
1003 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
1004
1005 VBOXCLIPBOARDEXTPARMS parms;
1006
1007 if (pfnExtension)
1008 {
1009 /* Install extension. */
1010 g_pfnExtension = pfnExtension;
1011 g_pvExtension = pvExtension;
1012
1013 parms.u.pfnCallback = extCallback;
1014 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1015 }
1016 else
1017 {
1018 if (g_pfnExtension)
1019 {
1020 parms.u.pfnCallback = NULL;
1021 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1022 }
1023
1024 /* Uninstall extension. */
1025 g_pfnExtension = NULL;
1026 g_pvExtension = NULL;
1027 }
1028
1029 return VINF_SUCCESS;
1030}
1031
1032extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *ptable)
1033{
1034 int rc = VINF_SUCCESS;
1035
1036 LogFlowFunc(("ptable=%p\n", ptable));
1037
1038 if (!ptable)
1039 {
1040 rc = VERR_INVALID_PARAMETER;
1041 }
1042 else
1043 {
1044 LogFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1045
1046 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1047 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1048 {
1049 rc = VERR_INVALID_PARAMETER;
1050 }
1051 else
1052 {
1053 g_pHelpers = ptable->pHelpers;
1054
1055 ptable->cbClient = sizeof(VBOXCLIPBOARDCLIENTDATA);
1056
1057 ptable->pfnUnload = svcUnload;
1058 ptable->pfnConnect = svcConnect;
1059 ptable->pfnDisconnect = svcDisconnect;
1060 ptable->pfnCall = svcCall;
1061 ptable->pfnHostCall = svcHostCall;
1062 ptable->pfnSaveState = svcSaveState;
1063 ptable->pfnLoadState = svcLoadState;
1064 ptable->pfnRegisterExtension = svcRegisterExtension;
1065 ptable->pfnNotify = NULL;
1066 ptable->pvService = NULL;
1067
1068 /* Service specific initialization. */
1069 rc = svcInit();
1070 }
1071 }
1072
1073 return rc;
1074}
Note: See TracBrowser for help on using the repository browser.

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