VirtualBox

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

Last change on this file since 79449 was 79366, 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: 49.5 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 79366 2019-06-26 15:59:30Z 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 * @section sec_uri_intro Transferring files.
72 *
73 * Since VBox x.x.x transferring files via Shared Clipboard is supported.
74 * See the VBOX_WITH_SHARED_CLIPBOARD_URI_LIST define for supported / enabled
75 * platforms.
76 *
77 * Copying files / directories from guest A to guest B requires the host
78 * service to act as a proxy and cache, as we don't allow direct VM-to-VM
79 * communication. Copying from / to the host also is taken into account.
80 *
81 * Support for VRDE (VRDP) is not implemented yet (see #9498).
82 *
83 * @section sec_uri_areas Clipboard areas.
84 *
85 * For larger / longer transfers there might be file data
86 * temporarily cached on the host, which has not been transferred to the
87 * destination yet. Such a cache is called a "Shared Clipboard Area", which
88 * in turn is identified by a unique ID across all VMs running on the same
89 * host. To control the access (and needed cleanup) of such clipboard areas,
90 * VBoxSVC (Main) is used for this task. A Shared Clipboard client can register,
91 * unregister, attach to and detach from a clipboard area. If all references
92 * to a clipboard area are released, a clipboard area gets detroyed automatically
93 * by VBoxSVC.
94 *
95 * By default a clipboard area lives in the user's temporary directory in the
96 * sub folder "VirtualBox Shared Clipboards/clipboard-<ID>". VBoxSVC does not
97 * do any file locking in a clipboard area, but keeps the clipboard areas's
98 * directory open to prevent deletion by third party processes.
99 *
100 * @todo We might use some VFS / container (IPRT?) for this instead of the
101 * host's file system directly?
102 *
103 * @section sec_uri_structure URI handling structure.
104 *
105 * All structures / classes are designed for running on both, on the guest
106 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
107 * duplication where applicable.
108 *
109 * Per HGCM client there is a so-called "URI context", which in turn can have
110 * one or mulitple so-called "URI transfer" objects. At the moment we only support
111 * on concurrent URI transfer per URI context. It's being used for reading from a
112 * source or writing to destination, depening on its direction. An URI transfer
113 * can have optional callbacks which might be needed by various implementations.
114 * Also, transfers optionally can run in an asynchronous thread to prevent
115 * blocking the UI while running.
116 *
117 * An URI transfer can maintain its own clipboard area; for the host service such
118 * a clipboard area is coupled to a clipboard area registered or attached with
119 * VBoxSVC.
120 *
121 * @section sec_uri_providers URI providers.
122 *
123 * For certain implementations (for example on Windows guests / hosts, using
124 * IDataObject and IStream objects) a more flexible approach reqarding reading /
125 * writing is needed. For this so-called URI providers abstract the way of how
126 * data is being read / written in the current context (host / guest), while
127 * the rest of the code stays the same.
128 *
129 */
130
131
132/*********************************************************************************************************************************
133* Header Files *
134*********************************************************************************************************************************/
135#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
136#include <VBox/log.h>
137
138#include <VBox/HostServices/VBoxClipboardSvc.h>
139#include <VBox/HostServices/VBoxClipboardExt.h>
140
141#include <iprt/alloc.h>
142#include <iprt/string.h>
143#include <iprt/assert.h>
144#include <iprt/critsect.h>
145
146#include <VBox/err.h>
147#include <VBox/vmm/ssm.h>
148
149#include "VBoxSharedClipboardSvc-internal.h"
150#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
151# include "VBoxSharedClipboardSvc-uri.h"
152#endif
153
154
155/*********************************************************************************************************************************
156* Prototypes *
157*********************************************************************************************************************************/
158static int vboxSvcClipboardClientStateInit(PVBOXCLIPBOARDCLIENTSTATE pState, uint32_t uClientID);
159static int vboxSvcClipboardClientStateDestroy(PVBOXCLIPBOARDCLIENTSTATE pState);
160static void vboxSvcClipboardClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pState);
161
162
163/*********************************************************************************************************************************
164* Global Variables *
165*********************************************************************************************************************************/
166static PVBOXHGCMSVCHELPERS g_pHelpers;
167
168static RTCRITSECT g_CritSect;
169static uint32_t g_uMode;
170
171PFNHGCMSVCEXT g_pfnExtension;
172void *g_pvExtension;
173
174static PVBOXCLIPBOARDCLIENTDATA g_pClientData;
175
176/* Serialization of data reading and format announcements from the RDP client. */
177static bool g_fReadingData = false;
178static bool g_fDelayedAnnouncement = false;
179static uint32_t g_u32DelayedFormats = 0;
180
181/** Is the clipboard running in headless mode? */
182static bool g_fHeadless = false;
183
184
185#if 0
186static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
187{
188 pParm->type = VBOX_HGCM_SVC_PARM_PTR;
189 pParm->u.pointer.size = cb;
190 pParm->u.pointer.addr = pv;
191}
192#endif
193
194static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
195{
196 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
197 {
198 *ppv = pParm->u.pointer.addr;
199 *pcb = pParm->u.pointer.size;
200 return VINF_SUCCESS;
201 }
202
203 return VERR_INVALID_PARAMETER;
204}
205
206uint32_t vboxSvcClipboardGetMode(void)
207{
208 return g_uMode;
209}
210
211#ifdef UNIT_TEST
212/** Testing interface, getter for clipboard mode */
213uint32_t TestClipSvcGetMode(void)
214{
215 return vboxSvcClipboardGetMode();
216}
217#endif
218
219/** Getter for headless setting. Also needed by testcase. */
220bool VBoxSvcClipboardGetHeadless(void)
221{
222 return g_fHeadless;
223}
224
225static void vboxSvcClipboardModeSet(uint32_t u32Mode)
226{
227 switch (u32Mode)
228 {
229 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
230 RT_FALL_THROUGH();
231 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
232 RT_FALL_THROUGH();
233 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
234 RT_FALL_THROUGH();
235 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
236 g_uMode = u32Mode;
237 break;
238
239 default:
240 g_uMode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
241 break;
242 }
243}
244
245bool VBoxSvcClipboardLock(void)
246{
247 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
248}
249
250void VBoxSvcClipboardUnlock(void)
251{
252 int rc2 = RTCritSectLeave(&g_CritSect);
253 AssertRC(rc2);
254}
255
256/* Set the HGCM parameters according to pending messages.
257 * Executed under the clipboard lock.
258 */
259static bool vboxSvcClipboardReturnMsg(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
260{
261 /** @todo r=andy The client at the moment supplies two parameters, which we can
262 * use by filling in the next message type sent by the host service.
263 * Make this more flexible later, as I don't want to break the existing protocol right now. */
264 if (cParms < 2)
265 {
266 AssertFailed(); /* Should never happen. */
267 return false;
268 }
269
270 /* Message priority is taken into account. */
271 if (pClientData->State.fHostMsgQuit)
272 {
273 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
274 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
275 HGCMSvcSetU32(&paParms[1], 0);
276 pClientData->State.fHostMsgQuit = false;
277 }
278 else if (pClientData->State.fHostMsgReadData)
279 {
280 uint32_t fFormat = 0;
281
282 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: u32RequestedFormat=%02X\n",
283 pClientData->State.u32RequestedFormat));
284
285 if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
286 fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
287 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
288 fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
289 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
290 fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
291#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
292 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
293 fFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
294#endif
295 else
296 {
297 LogRel2(("Clipboard: Unsupported format from guest (0x%x), skipping\n", fFormat));
298 pClientData->State.u32RequestedFormat = 0;
299 }
300 pClientData->State.u32RequestedFormat &= ~fFormat;
301 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
302 HGCMSvcSetU32(&paParms[1], fFormat);
303 if (pClientData->State.u32RequestedFormat == 0)
304 pClientData->State.fHostMsgReadData = false;
305 }
306 else if (pClientData->State.fHostMsgFormats)
307 {
308 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS: u32AvailableFormats=%02X\n",
309 pClientData->State.u32AvailableFormats));
310
311 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS);
312 HGCMSvcSetU32(&paParms[1], pClientData->State.u32AvailableFormats);
313 pClientData->State.fHostMsgFormats = false;
314 }
315#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
316 else if (vboxSvcClipboardURIReturnMsg(pClientData, cParms, paParms))
317 {
318 /* Nothing to do here yet. */
319 }
320#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
321 else
322 {
323 /* No pending messages. */
324 LogFlowFunc(("No pending message\n"));
325 return false;
326 }
327
328 /* Message information assigned. */
329 return true;
330}
331
332int vboxSvcClipboardReportMsg(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t uMsg, uint32_t uFormats)
333{
334 AssertPtrReturn(pClientData, VERR_INVALID_POINTER);
335
336 int rc = VINF_SUCCESS;
337
338 LogFlowFunc(("uMsg=%RU32, fIsAsync=%RTbool\n", uMsg, pClientData->State.fAsync));
339
340 if (VBoxSvcClipboardLock())
341 {
342 switch (uMsg)
343 {
344 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
345 {
346 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
347 pClientData->State.fHostMsgQuit = true;
348 } break;
349
350 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
351 {
352 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
353 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
354 {
355 /* Skip the message. */
356 break;
357 }
358
359 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: uFormats=%02X\n", uFormats));
360 pClientData->State.u32RequestedFormat = uFormats;
361 pClientData->State.fHostMsgReadData = true;
362 } break;
363
364 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
365 {
366 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
367 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
368 {
369 /* Skip the message. */
370 break;
371 }
372
373 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS: uFormats=%02X\n", uFormats));
374 pClientData->State.u32AvailableFormats = uFormats;
375 pClientData->State.fHostMsgFormats = true;
376 } break;
377
378 default:
379 {
380#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
381 rc = vboxSvcClipboardURIReportMsg(pClientData, uMsg, uFormats);
382#else
383 AssertMsgFailed(("Invalid message %RU32\n", uMsg));
384 rc = VERR_INVALID_PARAMETER;
385#endif
386 } break;
387 }
388
389 if (RT_SUCCESS(rc))
390 {
391 if (pClientData->State.fAsync)
392 {
393 /* The client waits for a response. */
394 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClientData,
395 pClientData->State.async.cParms,
396 pClientData->State.async.paParms);
397
398 /* Make a copy of the handle. */
399 VBOXHGCMCALLHANDLE callHandle = pClientData->State.async.callHandle;
400
401 if (fMessageReturned)
402 {
403 /* There is a response. */
404 pClientData->State.fAsync = false;
405 }
406
407 VBoxSvcClipboardUnlock();
408
409 if (fMessageReturned)
410 {
411 LogFlowFunc(("CallComplete\n"));
412 g_pHelpers->pfnCallComplete(callHandle, VINF_SUCCESS);
413 }
414 }
415 else
416 VBoxSvcClipboardUnlock();
417 }
418 else
419 VBoxSvcClipboardUnlock();
420 }
421
422 LogFlowFuncLeaveRC(rc);
423 return rc;
424}
425
426
427int vboxSvcClipboardSetSource(PVBOXCLIPBOARDCLIENTDATA pClientData, SHAREDCLIPBOARDSOURCE enmSource)
428{
429 if (!pClientData) /* If no client connected (anymore), bail out. */
430 return VINF_SUCCESS;
431
432 int rc = VINF_SUCCESS;
433
434 if (VBoxSvcClipboardLock())
435 {
436 pClientData->State.enmSource = enmSource;
437
438 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClientData->State.u32ClientID, pClientData->State.enmSource));
439
440 VBoxSvcClipboardUnlock();
441 }
442
443 LogFlowFuncLeaveRC(rc);
444 return rc;
445}
446
447static int svcInit(void)
448{
449 int rc = RTCritSectInit(&g_CritSect);
450
451 if (RT_SUCCESS(rc))
452 {
453 vboxSvcClipboardModeSet(VBOX_SHARED_CLIPBOARD_MODE_OFF);
454
455 rc = VBoxClipboardSvcImplInit();
456
457 /* Clean up on failure, because 'svnUnload' will not be called
458 * if the 'svcInit' returns an error.
459 */
460 if (RT_FAILURE(rc))
461 {
462 RTCritSectDelete(&g_CritSect);
463 }
464 }
465
466 return rc;
467}
468
469static DECLCALLBACK(int) svcUnload(void *)
470{
471 VBoxClipboardSvcImplDestroy();
472 RTCritSectDelete(&g_CritSect);
473 return VINF_SUCCESS;
474}
475
476/**
477 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
478 * to the guest side.
479 */
480static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
481{
482 RT_NOREF(u32ClientID);
483
484 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
485
486 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
487
488 vboxSvcClipboardReportMsg(pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0); /** @todo r=andy Why is this necessary? The client already disconnected ... */
489
490 vboxSvcClipboardCompleteReadData(pClientData, VERR_NO_DATA, 0);
491
492#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
493 PSHAREDCLIPBOARDURITRANSFER pTransfer = SharedClipboardURICtxGetTransfer(&pClientData->URI, 0 /* Index*/);
494 if (pTransfer)
495 vboxSvcClipboardURIAreaDetach(&pClientData->State, pTransfer);
496
497 SharedClipboardURICtxDestroy(&pClientData->URI);
498#endif
499
500 VBoxClipboardSvcImplDisconnect(pClientData);
501
502 vboxSvcClipboardClientStateReset(&pClientData->State);
503 vboxSvcClipboardClientStateDestroy(&pClientData->State);
504
505 g_pClientData = NULL;
506
507 return VINF_SUCCESS;
508}
509
510static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
511{
512 RT_NOREF(fRequestor, fRestoring);
513
514 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
515
516 LogFlowFunc(("u32ClientID=%RU32\n", u32ClientID));
517
518 /* If there is already a client connected then we want to release it first. */
519 if (g_pClientData != NULL)
520 {
521 uint32_t uOldClientID = g_pClientData->State.u32ClientID;
522 if (uOldClientID)
523 {
524 svcDisconnect(NULL, uOldClientID, g_pClientData);
525
526 /* And free the resources in the HGCM subsystem. */
527 g_pHelpers->pfnDisconnectClient(g_pHelpers->pvInstance, uOldClientID);
528 }
529 }
530
531 /* Reset the client state. */
532 vboxSvcClipboardClientStateReset(&pClientData->State);
533
534 /* (Re-)initialize the client state. */
535 vboxSvcClipboardClientStateInit(&pClientData->State, u32ClientID);
536
537 int rc = VBoxClipboardSvcImplConnect(pClientData, VBoxSvcClipboardGetHeadless());
538#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
539 if (RT_SUCCESS(rc))
540 rc = SharedClipboardURICtxInit(&pClientData->URI);
541#endif
542
543 if (RT_SUCCESS(rc))
544 {
545 g_pClientData = pClientData;
546 }
547
548 LogFlowFuncLeaveRC(rc);
549 return rc;
550}
551
552static DECLCALLBACK(void) svcCall(void *,
553 VBOXHGCMCALLHANDLE callHandle,
554 uint32_t u32ClientID,
555 void *pvClient,
556 uint32_t u32Function,
557 uint32_t cParms,
558 VBOXHGCMSVCPARM paParms[],
559 uint64_t tsArrival)
560{
561 RT_NOREF(u32ClientID, tsArrival);
562
563 int rc = VINF_SUCCESS;
564
565 LogFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=%p\n",
566 u32ClientID, u32Function, cParms, paParms));
567
568 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
569
570#ifdef DEBUG
571 uint32_t i;
572
573 for (i = 0; i < cParms; i++)
574 {
575 /** @todo parameters other than 32 bit */
576 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
577 }
578#endif
579
580 bool fAsynchronousProcessing = false;
581
582 switch (u32Function)
583 {
584 case VBOX_SHARED_CLIPBOARD_GUEST_FN_GET_HOST_MSG:
585 {
586 /* The quest requests a host message. */
587 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_GET_HOST_MSG\n"));
588
589 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG)
590 {
591 rc = VERR_INVALID_PARAMETER;
592 }
593 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
594 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
595 {
596 rc = VERR_INVALID_PARAMETER;
597 }
598 else
599 {
600 /* Atomically verify the client's state. */
601 if (VBoxSvcClipboardLock())
602 {
603 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClientData, cParms, paParms);
604 if (fMessageReturned)
605 {
606 /* Just return to the caller. */
607 pClientData->State.fAsync = false;
608 }
609 else
610 {
611 /* No event available at the time. Process asynchronously. */
612 fAsynchronousProcessing = true;
613
614 pClientData->State.fAsync = true;
615 pClientData->State.async.callHandle = callHandle;
616 pClientData->State.async.cParms = cParms;
617 pClientData->State.async.paParms = paParms;
618 }
619
620 VBoxSvcClipboardUnlock();
621 }
622 else
623 {
624 rc = VERR_NOT_SUPPORTED;
625 }
626 }
627 } break;
628
629 case VBOX_SHARED_CLIPBOARD_GUEST_FN_REPORT_FORMATS:
630 {
631 /* The guest reports that some formats are available. */
632 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_REPORT_FORMATS\n"));
633
634 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_REPORT_FORMATS)
635 {
636 rc = VERR_INVALID_PARAMETER;
637 }
638 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
639 {
640 rc = VERR_INVALID_PARAMETER;
641 }
642 else
643 {
644 uint32_t u32Formats;
645 rc = HGCMSvcGetU32(&paParms[0], &u32Formats);
646 if (RT_SUCCESS(rc))
647 {
648 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
649 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
650 {
651 rc = VERR_ACCESS_DENIED;
652 }
653 else
654 {
655 rc = vboxSvcClipboardSetSource(pClientData, SHAREDCLIPBOARDSOURCE_REMOTE);
656
657#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
658 if ( RT_SUCCESS(rc)
659 && (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST))
660 {
661 if (!SharedClipboardURICtxTransfersMaximumReached(&pClientData->URI))
662 {
663 SharedClipboardURICtxTransfersCleanup(&pClientData->URI);
664
665 PSHAREDCLIPBOARDURITRANSFER pTransfer;
666 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_READ,
667 SHAREDCLIPBOARDSOURCE_REMOTE, &pTransfer);
668 if (RT_SUCCESS(rc))
669 {
670 rc = vboxSvcClipboardURIAreaRegister(&pClientData->State, pTransfer);
671 if (RT_SUCCESS(rc))
672 {
673 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
674 RT_ZERO(creationCtx);
675
676 creationCtx.enmSource = pClientData->State.enmSource;
677
678 RT_ZERO(creationCtx.Interface);
679 creationCtx.Interface.pfnReadDataHdr = VBoxSvcClipboardProviderImplURIReadDataHdr;
680 creationCtx.Interface.pfnReadDataChunk = VBoxSvcClipboardProviderImplURIReadDataChunk;
681 creationCtx.Interface.pfnReadDirectory = VBoxSvcClipboardProviderImplURIReadDir;
682 creationCtx.Interface.pfnReadFileHdr = VBoxSvcClipboardProviderImplURIReadFileHdr;
683 creationCtx.Interface.pfnReadFileData = VBoxSvcClipboardProviderImplURIReadFileData;
684
685 creationCtx.pvUser = pClientData;
686
687 /* Register needed callbacks so that we can wait for the meta data to arrive here. */
688 SHAREDCLIPBOARDURITRANSFERCALLBACKS Callbacks;
689 RT_ZERO(Callbacks);
690
691 Callbacks.pvUser = pClientData;
692
693 Callbacks.pfnTransferPrepare = VBoxSvcClipboardURITransferPrepareCallback;
694 Callbacks.pfnDataHeaderComplete = VBoxSvcClipboardURIDataHeaderCompleteCallback;
695 Callbacks.pfnDataComplete = VBoxSvcClipboardURIDataCompleteCallback;
696 Callbacks.pfnTransferComplete = VBoxSvcClipboardURITransferCompleteCallback;
697 Callbacks.pfnTransferCanceled = VBoxSvcClipboardURITransferCanceledCallback;
698 Callbacks.pfnTransferError = VBoxSvcClipboardURITransferErrorCallback;
699
700 SharedClipboardURITransferSetCallbacks(pTransfer, &Callbacks);
701
702 rc = SharedClipboardURITransferProviderCreate(pTransfer, &creationCtx);
703 if (RT_SUCCESS(rc))
704 rc = SharedClipboardURICtxTransferAdd(&pClientData->URI, pTransfer);
705 }
706
707 if (RT_SUCCESS(rc))
708 {
709 rc = VBoxClipboardSvcImplURITransferCreate(pClientData, pTransfer);
710 }
711 else
712 {
713 VBoxClipboardSvcImplURITransferDestroy(pClientData, pTransfer);
714 SharedClipboardURITransferDestroy(pTransfer);
715 }
716 }
717 }
718 else
719 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
720
721 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_URI_LIST: %Rrc\n", rc));
722
723 if (RT_FAILURE(rc))
724 LogRel(("Shared Clipboard: Initializing URI guest to host read transfer failed with %Rrc\n", rc));
725 }
726#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
727
728 if (RT_SUCCESS(rc))
729 {
730 if (g_pfnExtension)
731 {
732 VBOXCLIPBOARDEXTPARMS parms;
733 RT_ZERO(parms);
734 parms.u32Format = u32Formats;
735
736 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
737 }
738
739 rc = VBoxClipboardSvcImplFormatAnnounce(pClientData, u32Formats);
740 }
741 }
742 }
743 }
744 } break;
745
746 case VBOX_SHARED_CLIPBOARD_GUEST_FN_READ_DATA:
747 {
748 /* The guest wants to read data in the given format. */
749 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_READ_DATA\n"));
750
751 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
752 {
753 rc = VERR_INVALID_PARAMETER;
754 }
755 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
756 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
757 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
758 )
759 {
760 rc = VERR_INVALID_PARAMETER;
761 }
762 else
763 {
764 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
765 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
766 {
767 rc = VERR_ACCESS_DENIED;
768 break;
769 }
770
771 uint32_t u32Format;
772 rc = HGCMSvcGetU32(&paParms[0], &u32Format);
773 if (RT_SUCCESS(rc))
774 {
775#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
776 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
777 {
778 if (!SharedClipboardURICtxTransfersMaximumReached(&pClientData->URI))
779 {
780 SharedClipboardURICtxTransfersCleanup(&pClientData->URI);
781
782 PSHAREDCLIPBOARDURITRANSFER pTransfer;
783 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_WRITE,
784 pClientData->State.enmSource,
785 &pTransfer);
786 if (RT_SUCCESS(rc))
787 {
788 /* Attach to the most recent clipboard area. */
789 rc = vboxSvcClipboardURIAreaAttach(&pClientData->State, pTransfer, 0 /* Area ID */);
790 if (RT_SUCCESS(rc))
791 {
792 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
793 RT_ZERO(creationCtx);
794
795 creationCtx.enmSource = SharedClipboardURITransferGetSource(pTransfer);
796
797 RT_ZERO(creationCtx.Interface);
798
799 creationCtx.Interface.pfnWriteDataHdr = VBoxSvcClipboardProviderImplURIWriteDataHdr;
800 creationCtx.Interface.pfnWriteDataChunk = VBoxSvcClipboardProviderImplURIWriteDataChunk;
801 creationCtx.Interface.pfnWriteDirectory = VBoxSvcClipboardProviderImplURIWriteDir;
802 creationCtx.Interface.pfnWriteFileHdr = VBoxSvcClipboardProviderImplURIWriteFileHdr;
803 creationCtx.Interface.pfnWriteFileData = VBoxSvcClipboardProviderImplURIWriteFileData;
804
805 creationCtx.pvUser = pClientData;
806
807 rc = SharedClipboardURITransferProviderCreate(pTransfer, &creationCtx);
808 if (RT_SUCCESS(rc))
809 rc = SharedClipboardURICtxTransferAdd(&pClientData->URI, pTransfer);
810 }
811
812 if (RT_SUCCESS(rc))
813 {
814 rc = VBoxClipboardSvcImplURITransferCreate(pClientData, pTransfer);
815 }
816 else
817 {
818 VBoxClipboardSvcImplURITransferDestroy(pClientData, pTransfer);
819 SharedClipboardURITransferDestroy(pTransfer);
820 }
821 }
822 }
823 else
824 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
825
826 if (RT_FAILURE(rc))
827 LogRel(("Shared Clipboard: Initializing URI host to guest write transfer failed with %Rrc\n", rc));
828 }
829 else
830 {
831#endif
832 void *pv;
833 uint32_t cb;
834 rc = VBoxHGCMParmPtrGet(&paParms[1], &pv, &cb);
835 if (RT_SUCCESS(rc))
836 {
837 uint32_t cbActual = 0;
838
839 if (g_pfnExtension)
840 {
841 VBOXCLIPBOARDEXTPARMS parms;
842 RT_ZERO(parms);
843
844 parms.u32Format = u32Format;
845 parms.u.pvData = pv;
846 parms.cbData = cb;
847
848 g_fReadingData = true;
849
850 rc = g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
851 LogFlowFunc(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
852
853 if (g_fDelayedAnnouncement)
854 {
855 vboxSvcClipboardReportMsg(g_pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, g_u32DelayedFormats);
856 g_fDelayedAnnouncement = false;
857 g_u32DelayedFormats = 0;
858 }
859
860 g_fReadingData = false;
861
862 if (RT_SUCCESS (rc))
863 {
864 cbActual = parms.cbData;
865 }
866 }
867
868 /* Release any other pending read, as we only
869 * support one pending read at one time. */
870 rc = vboxSvcClipboardCompleteReadData(pClientData, VERR_NO_DATA, 0);
871 if (RT_SUCCESS(rc))
872 rc = VBoxClipboardSvcImplReadData(pClientData, u32Format, pv, cb, &cbActual);
873
874 /* Remember our read request until it is completed.
875 * See the protocol description above for more
876 * information. */
877 if (rc == VINF_HGCM_ASYNC_EXECUTE)
878 {
879 if (VBoxSvcClipboardLock())
880 {
881 pClientData->State.asyncRead.callHandle = callHandle;
882 pClientData->State.asyncRead.cParms = cParms;
883 pClientData->State.asyncRead.paParms = paParms;
884 pClientData->State.fReadPending = true;
885 fAsynchronousProcessing = true;
886 VBoxSvcClipboardUnlock();
887 }
888 else
889 rc = VERR_NOT_SUPPORTED;
890 }
891 else if (RT_SUCCESS (rc))
892 {
893 HGCMSvcSetU32(&paParms[2], cbActual);
894 }
895 }
896#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
897 }
898#endif
899 }
900 }
901 } break;
902
903 case VBOX_SHARED_CLIPBOARD_GUEST_FN_WRITE_DATA:
904 {
905 /* The guest writes the requested data. */
906 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_WRITE_DATA\n"));
907
908 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
909 {
910 rc = VERR_INVALID_PARAMETER;
911 }
912 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
913 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
914 )
915 {
916 rc = VERR_INVALID_PARAMETER;
917 }
918 else
919 {
920 void *pv;
921 uint32_t cb;
922 uint32_t u32Format;
923
924 rc = HGCMSvcGetU32(&paParms[0], &u32Format);
925 if (RT_SUCCESS(rc))
926 {
927 rc = VBoxHGCMParmPtrGet(&paParms[1], &pv, &cb);
928 if (RT_SUCCESS(rc))
929 {
930 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
931 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
932 {
933 rc = VERR_NOT_SUPPORTED;
934 break;
935 }
936
937 if (g_pfnExtension)
938 {
939 VBOXCLIPBOARDEXTPARMS parms;
940 RT_ZERO(parms);
941
942 parms.u32Format = u32Format;
943 parms.u.pvData = pv;
944 parms.cbData = cb;
945
946 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
947 }
948
949 rc = VBoxClipboardSvcImplWriteData(pClientData, pv, cb, u32Format);
950 }
951 }
952 }
953 } break;
954
955 default:
956 {
957#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
958 rc = vboxSvcClipboardURIHandler(u32ClientID, pvClient, u32Function, cParms, paParms, tsArrival,
959 &fAsynchronousProcessing);
960#else
961 rc = VERR_NOT_IMPLEMENTED;
962#endif
963 } break;
964 }
965
966 LogFlowFunc(("fAsynchronousProcessing=%RTbool\n", fAsynchronousProcessing));
967
968 if (!fAsynchronousProcessing)
969 g_pHelpers->pfnCallComplete(callHandle, rc);
970
971 LogFlowFuncLeaveRC(rc);
972}
973
974/** If the client in the guest is waiting for a read operation to complete
975 * then complete it, otherwise return. See the protocol description in the
976 * shared clipboard module description. */
977int vboxSvcClipboardCompleteReadData(PVBOXCLIPBOARDCLIENTDATA pClientData, int rc, uint32_t cbActual)
978{
979 VBOXHGCMCALLHANDLE callHandle = NULL;
980 VBOXHGCMSVCPARM *paParms = NULL;
981 bool fReadPending = false;
982 if (VBoxSvcClipboardLock()) /* if not can we do anything useful? */
983 {
984 callHandle = pClientData->State.asyncRead.callHandle;
985 paParms = pClientData->State.asyncRead.paParms;
986 fReadPending = pClientData->State.fReadPending;
987 pClientData->State.fReadPending = false;
988 VBoxSvcClipboardUnlock();
989 }
990 if (fReadPending)
991 {
992 HGCMSvcSetU32(&paParms[2], cbActual);
993 g_pHelpers->pfnCallComplete(callHandle, rc);
994 }
995
996 return VINF_SUCCESS;
997}
998
999/**
1000 * Initializes a Shared Clipboard service's client state.
1001 *
1002 * @returns VBox status code.
1003 * @param pState Client state to initialize.
1004 * @param uClientID Client ID (HGCM) to use for this client state.
1005 */
1006static int vboxSvcClipboardClientStateInit(PVBOXCLIPBOARDCLIENTSTATE pState, uint32_t uClientID)
1007{
1008 LogFlowFuncEnter();
1009
1010 /* Register the client.
1011 * Note: Do *not* memset the struct, as it contains classes (for caching). */
1012 pState->u32ClientID = uClientID;
1013
1014 return VINF_SUCCESS;
1015}
1016
1017/**
1018 * Destroys a Shared Clipboard service's client state.
1019 *
1020 * @returns VBox status code.
1021 * @param pState Client state to destroy.
1022 */
1023static int vboxSvcClipboardClientStateDestroy(PVBOXCLIPBOARDCLIENTSTATE pState)
1024{
1025 LogFlowFuncEnter();
1026
1027 RT_NOREF(pState);
1028
1029 return VINF_SUCCESS;
1030}
1031
1032/**
1033 * Resets a Shared Clipboard service's client state.
1034 *
1035 * @param pState Client state to reset.
1036 */
1037static void vboxSvcClipboardClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pState)
1038{
1039 LogFlowFuncEnter();
1040
1041 /** @todo Clear async / asynRead / ... data? */
1042
1043 pState->u32ClientID = 0;
1044 pState->fAsync = false;
1045 pState->fHostMsgFormats = false;
1046 pState->fHostMsgQuit = false;
1047 pState->fHostMsgReadData = false;
1048 pState->fReadPending = false;
1049 pState->u32AvailableFormats = 0;
1050 pState->u32RequestedFormat = 0;
1051}
1052
1053/*
1054 * We differentiate between a function handler for the guest and one for the host.
1055 */
1056static DECLCALLBACK(int) svcHostCall(void *,
1057 uint32_t u32Function,
1058 uint32_t cParms,
1059 VBOXHGCMSVCPARM paParms[])
1060{
1061 int rc = VINF_SUCCESS;
1062
1063 LogFunc(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
1064 u32Function, cParms, paParms));
1065
1066 switch (u32Function)
1067 {
1068 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
1069 {
1070 LogFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
1071
1072 if (cParms != 1)
1073 {
1074 rc = VERR_INVALID_PARAMETER;
1075 }
1076 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
1077 )
1078 {
1079 rc = VERR_INVALID_PARAMETER;
1080 }
1081 else
1082 {
1083 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
1084
1085 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
1086
1087 /* The setter takes care of invalid values. */
1088 vboxSvcClipboardModeSet(u32Mode);
1089 }
1090 } break;
1091
1092 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
1093 {
1094 uint32_t u32Headless = g_fHeadless;
1095
1096 rc = VERR_INVALID_PARAMETER;
1097 if (cParms != 1)
1098 break;
1099
1100 rc = HGCMSvcGetU32(&paParms[0], &u32Headless);
1101 if (RT_SUCCESS(rc))
1102 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
1103 (unsigned) u32Headless));
1104
1105 g_fHeadless = RT_BOOL(u32Headless);
1106
1107 } break;
1108
1109 default:
1110 {
1111#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1112 rc = vboxSvcClipboardURIHostHandler(u32Function, cParms, paParms);
1113#endif
1114 } break;
1115 }
1116
1117 LogFlowFunc(("svcHostCall: rc = %Rrc\n", rc));
1118 return rc;
1119}
1120
1121#ifndef UNIT_TEST
1122/**
1123 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
1124 */
1125static SSMFIELD const g_aClipboardClientDataFields[] =
1126{
1127 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, u32ClientID), /* for validation purposes */
1128 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgQuit),
1129 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgReadData),
1130 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgFormats),
1131 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, u32RequestedFormat),
1132 SSMFIELD_ENTRY_TERM()
1133};
1134#endif
1135
1136static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
1137{
1138 RT_NOREF(u32ClientID);
1139
1140#ifndef UNIT_TEST
1141 /*
1142 * When the state will be restored, pending requests will be reissued
1143 * by VMMDev. The service therefore must save state as if there were no
1144 * pending request.
1145 * Pending requests, if any, will be completed in svcDisconnect.
1146 */
1147 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1148
1149 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
1150
1151 /* This field used to be the length. We're using it as a version field
1152 with the high bit set. */
1153 SSMR3PutU32(pSSM, UINT32_C(0x80000002));
1154 int rc = SSMR3PutStructEx(pSSM, pClientData, sizeof(*pClientData), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1155 AssertRCReturn(rc, rc);
1156
1157#else /* UNIT_TEST */
1158 RT_NOREF3(u32ClientID, pvClient, pSSM);
1159#endif /* UNIT_TEST */
1160 return VINF_SUCCESS;
1161}
1162
1163static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
1164{
1165#ifndef UNIT_TEST
1166 RT_NOREF(u32ClientID, uVersion);
1167
1168 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1169
1170 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
1171 AssertPtr(pClientData);
1172
1173 /* Existing client can not be in async state yet. */
1174 Assert(!pClientData->State.fAsync);
1175
1176 /* Save the client ID for data validation. */
1177 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
1178 uint32_t const u32ClientIDOld = pClientData->State.u32ClientID;
1179
1180 /* Restore the client data. */
1181 uint32_t lenOrVer;
1182 int rc = SSMR3GetU32(pSSM, &lenOrVer);
1183 AssertRCReturn(rc, rc);
1184 if (lenOrVer == UINT32_C(0x80000002))
1185 {
1186 rc = SSMR3GetStructEx(pSSM, pClientData, sizeof(*pClientData), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1187 AssertRCReturn(rc, rc);
1188 }
1189 else
1190 {
1191 LogFunc(("Client data size mismatch: got %#x\n", lenOrVer));
1192 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1193 }
1194
1195 /* Verify the client ID. */
1196 if (pClientData->State.u32ClientID != u32ClientIDOld)
1197 {
1198 LogFunc(("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClientData->State.u32ClientID));
1199 pClientData->State.u32ClientID = u32ClientIDOld;
1200 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1201 }
1202
1203 /* Actual host data are to be reported to guest (SYNC). */
1204 VBoxClipboardSvcImplSync(pClientData);
1205
1206#else /* UNIT_TEST*/
1207 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
1208#endif /* UNIT_TEST */
1209 return VINF_SUCCESS;
1210}
1211
1212static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
1213{
1214 RT_NOREF(pvData, cbData);
1215
1216 LogFlowFunc(("u32Function=%RU32\n", u32Function));
1217
1218 int rc = VINF_SUCCESS;
1219
1220 if (g_pClientData != NULL)
1221 {
1222 switch (u32Function)
1223 {
1224 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
1225 {
1226 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_fReadingData=%RTbool\n", g_fReadingData));
1227 if (g_fReadingData)
1228 {
1229 g_fDelayedAnnouncement = true;
1230 g_u32DelayedFormats = u32Format;
1231 }
1232 #if 0
1233 else
1234 {
1235 vboxSvcClipboardReportMsg(g_pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, u32Format);
1236 }
1237 #endif
1238 } break;
1239
1240#if 0
1241 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
1242 {
1243 vboxSvcClipboardReportMsg(g_pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
1244 } break;
1245#endif
1246
1247 default:
1248 /* Just skip other messages. */
1249 break;
1250 }
1251 }
1252
1253 LogFlowFuncLeaveRC(rc);
1254 return rc;
1255}
1256
1257static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
1258{
1259 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
1260
1261 VBOXCLIPBOARDEXTPARMS parms;
1262 RT_ZERO(parms);
1263
1264 if (pfnExtension)
1265 {
1266 /* Install extension. */
1267 g_pfnExtension = pfnExtension;
1268 g_pvExtension = pvExtension;
1269
1270 parms.u.pfnCallback = extCallback;
1271 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1272 }
1273 else
1274 {
1275 if (g_pfnExtension)
1276 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1277
1278 /* Uninstall extension. */
1279 g_pfnExtension = NULL;
1280 g_pvExtension = NULL;
1281 }
1282
1283 return VINF_SUCCESS;
1284}
1285
1286extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
1287{
1288 int rc = VINF_SUCCESS;
1289
1290 LogFlowFunc(("ptable=%p\n", ptable));
1291
1292 if (!ptable)
1293 {
1294 rc = VERR_INVALID_PARAMETER;
1295 }
1296 else
1297 {
1298 LogFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1299
1300 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1301 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1302 {
1303 rc = VERR_INVALID_PARAMETER;
1304 }
1305 else
1306 {
1307 g_pHelpers = ptable->pHelpers;
1308
1309 ptable->cbClient = sizeof(VBOXCLIPBOARDCLIENTDATA);
1310
1311 ptable->pfnUnload = svcUnload;
1312 ptable->pfnConnect = svcConnect;
1313 ptable->pfnDisconnect = svcDisconnect;
1314 ptable->pfnCall = svcCall;
1315 ptable->pfnHostCall = svcHostCall;
1316 ptable->pfnSaveState = svcSaveState;
1317 ptable->pfnLoadState = svcLoadState;
1318 ptable->pfnRegisterExtension = svcRegisterExtension;
1319 ptable->pfnNotify = NULL;
1320 ptable->pvService = NULL;
1321
1322 /* Service specific initialization. */
1323 rc = svcInit();
1324 }
1325 }
1326
1327 return rc;
1328}
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