VirtualBox

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

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