VirtualBox

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

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