VirtualBox

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

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