VirtualBox

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

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