VirtualBox

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

Last change on this file since 78824 was 78810, checked in by vboxsync, 6 years ago

Shared Clipboard/HostSvc: Build fix.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette