VirtualBox

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

Last change on this file since 79497 was 79497, 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: 60.4 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 79497 2019-07-03 13:28: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 * @section sec_uri_intro Transferring files.
72 *
73 * Since VBox x.x.x transferring files via Shared Clipboard is supported.
74 * See the VBOX_WITH_SHARED_CLIPBOARD_URI_LIST define for supported / enabled
75 * platforms.
76 *
77 * Copying files / directories from guest A to guest B requires the host
78 * service to act as a proxy and cache, as we don't allow direct VM-to-VM
79 * communication. Copying from / to the host also is taken into account.
80 *
81 * Support for VRDE (VRDP) is not implemented yet (see #9498).
82 *
83 * @section sec_uri_areas Clipboard areas.
84 *
85 * For larger / longer transfers there might be file data
86 * temporarily cached on the host, which has not been transferred to the
87 * destination yet. Such a cache is called a "Shared Clipboard Area", which
88 * in turn is identified by a unique ID across all VMs running on the same
89 * host. To control the access (and needed cleanup) of such clipboard areas,
90 * VBoxSVC (Main) is used for this task. A Shared Clipboard client can register,
91 * unregister, attach to and detach from a clipboard area. If all references
92 * to a clipboard area are released, a clipboard area gets detroyed automatically
93 * by VBoxSVC.
94 *
95 * By default a clipboard area lives in the user's temporary directory in the
96 * sub folder "VirtualBox Shared Clipboards/clipboard-<ID>". VBoxSVC does not
97 * do any file locking in a clipboard area, but keeps the clipboard areas's
98 * directory open to prevent deletion by third party processes.
99 *
100 * @todo We might use some VFS / container (IPRT?) for this instead of the
101 * host's file system directly?
102 *
103 * @section sec_uri_structure URI handling structure.
104 *
105 * All structures / classes are designed for running on both, on the guest
106 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
107 * duplication where applicable.
108 *
109 * Per HGCM client there is a so-called "URI context", which in turn can have
110 * one or mulitple so-called "URI transfer" objects. At the moment we only support
111 * on concurrent URI transfer per URI context. It's being used for reading from a
112 * source or writing to destination, depening on its direction. An URI transfer
113 * can have optional callbacks which might be needed by various implementations.
114 * Also, transfers optionally can run in an asynchronous thread to prevent
115 * blocking the UI while running.
116 *
117 * An URI transfer can maintain its own clipboard area; for the host service such
118 * a clipboard area is coupled to a clipboard area registered or attached with
119 * VBoxSVC.
120 *
121 * @section sec_uri_providers URI providers.
122 *
123 * For certain implementations (for example on Windows guests / hosts, using
124 * IDataObject and IStream objects) a more flexible approach reqarding reading /
125 * writing is needed. For this so-called URI providers abstract the way of how
126 * data is being read / written in the current context (host / guest), while
127 * the rest of the code stays the same.
128 *
129 */
130
131
132/*********************************************************************************************************************************
133* Header Files *
134*********************************************************************************************************************************/
135#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
136#include <VBox/log.h>
137
138#include <VBox/HostServices/Service.h>
139#include <VBox/HostServices/VBoxClipboardSvc.h>
140#include <VBox/HostServices/VBoxClipboardExt.h>
141
142#include <iprt/alloc.h>
143#include <iprt/string.h>
144#include <iprt/assert.h>
145#include <iprt/critsect.h>
146
147#include <VBox/err.h>
148#include <VBox/vmm/ssm.h>
149
150#include "VBoxSharedClipboardSvc-internal.h"
151#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
152# include "VBoxSharedClipboardSvc-uri.h"
153#endif
154
155using namespace HGCM;
156
157
158/*********************************************************************************************************************************
159* Prototypes *
160*********************************************************************************************************************************/
161static int vboxSvcClipboardClientStateInit(PVBOXCLIPBOARDCLIENTSTATE pState, uint32_t uClientID);
162static int vboxSvcClipboardClientStateDestroy(PVBOXCLIPBOARDCLIENTSTATE pState);
163static void vboxSvcClipboardClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pState);
164
165
166/*********************************************************************************************************************************
167* Global Variables *
168*********************************************************************************************************************************/
169static PVBOXHGCMSVCHELPERS g_pHelpers;
170
171static RTCRITSECT g_CritSect;
172static uint32_t g_uMode;
173
174PFNHGCMSVCEXT g_pfnExtension;
175void *g_pvExtension;
176
177/* Serialization of data reading and format announcements from the RDP client. */
178static bool g_fReadingData = false;
179static bool g_fDelayedAnnouncement = false;
180static uint32_t g_u32DelayedFormats = 0;
181
182/** Is the clipboard running in headless mode? */
183static bool g_fHeadless = false;
184
185/** Map of all connected clients. */
186ClipboardClientMap g_mapClients;
187
188/** List of all clients which are queued up (deferred return) and ready
189 * to process new commands. The key is the (unique) client ID. */
190ClipboardClientQueue g_listClientsDeferred;
191
192
193#if 0
194static void VBoxHGCMParmPtrSet (VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
195{
196 pParm->type = VBOX_HGCM_SVC_PARM_PTR;
197 pParm->u.pointer.size = cb;
198 pParm->u.pointer.addr = pv;
199}
200#endif
201
202static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
203{
204 if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
205 {
206 *ppv = pParm->u.pointer.addr;
207 *pcb = pParm->u.pointer.size;
208 return VINF_SUCCESS;
209 }
210
211 return VERR_INVALID_PARAMETER;
212}
213
214uint32_t vboxSvcClipboardGetMode(void)
215{
216 return g_uMode;
217}
218
219#ifdef UNIT_TEST
220/** Testing interface, getter for clipboard mode */
221uint32_t TestClipSvcGetMode(void)
222{
223 return vboxSvcClipboardGetMode();
224}
225#endif
226
227/** Getter for headless setting. Also needed by testcase. */
228bool VBoxSvcClipboardGetHeadless(void)
229{
230 return g_fHeadless;
231}
232
233static void vboxSvcClipboardModeSet(uint32_t u32Mode)
234{
235 switch (u32Mode)
236 {
237 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
238 RT_FALL_THROUGH();
239 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
240 RT_FALL_THROUGH();
241 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
242 RT_FALL_THROUGH();
243 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
244 g_uMode = u32Mode;
245 break;
246
247 default:
248 g_uMode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
249 break;
250 }
251}
252
253bool VBoxSvcClipboardLock(void)
254{
255 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
256}
257
258void VBoxSvcClipboardUnlock(void)
259{
260 int rc2 = RTCritSectLeave(&g_CritSect);
261 AssertRC(rc2);
262}
263
264/**
265 * Resets a client's state message queue.
266 *
267 * @param pState Pointer to the client's state structure to reset message queue for.
268 */
269void vboxSvcClipboardMsgQueueReset(PVBOXCLIPBOARDCLIENTSTATE pState)
270{
271 LogFlowFuncEnter();
272
273 while (!pState->queueMsg.isEmpty())
274 {
275 RTMemFree(pState->queueMsg.last());
276 pState->queueMsg.removeLast();
277 }
278}
279
280/**
281 * Allocates a new clipboard message.
282 *
283 * @returns Allocated clipboard message, or NULL on failure.
284 * @param uMsg Message type of message to allocate.
285 * @param cParms Number of HGCM parameters to allocate.
286 */
287PVBOXCLIPBOARDCLIENTMSG vboxSvcClipboardMsgAlloc(uint32_t uMsg, uint32_t cParms)
288{
289 PVBOXCLIPBOARDCLIENTMSG pMsg = (PVBOXCLIPBOARDCLIENTMSG)RTMemAlloc(sizeof(VBOXCLIPBOARDCLIENTMSG));
290 if (pMsg)
291 {
292 pMsg->m_paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * cParms);
293 if (pMsg->m_paParms)
294 {
295 pMsg->m_cParms = cParms;
296 pMsg->m_uMsg = uMsg;
297
298 return pMsg;
299 }
300 }
301
302 RTMemFree(pMsg);
303 return NULL;
304}
305
306/**
307 * Frees a formerly allocated clipboard message.
308 *
309 * @param pMsg Clipboard message to free.
310 * The pointer will be invalid after calling this function.
311 */
312void vboxSvcClipboardMsgFree(PVBOXCLIPBOARDCLIENTMSG pMsg)
313{
314 if (!pMsg)
315 return;
316
317 if (pMsg->m_paParms)
318 RTMemFree(pMsg->m_paParms);
319
320 RTMemFree(pMsg);
321 pMsg = NULL;
322}
323
324/**
325 * Adds a new message to a client'S message queue.
326 *
327 * @returns IPRT status code.
328 * @param pState Pointer to the client's state structure to add new message to.
329 * @param pMsg Pointer to message to add. The queue then owns the pointer.
330 * @param fAppend Whether to append or prepend the message to the queue.
331 */
332int vboxSvcClipboardMsgAdd(PVBOXCLIPBOARDCLIENTSTATE pState, PVBOXCLIPBOARDCLIENTMSG pMsg, bool fAppend)
333{
334 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
335
336 LogFlowFunc(("uMsg=%RU32, cParms=%RU32, fAppend=%RTbool\n", pMsg->m_uMsg, pMsg->m_cParms, fAppend));
337
338 if (fAppend)
339 pState->queueMsg.append(pMsg);
340 else
341 pState->queueMsg.prepend(pMsg);
342
343 /** @todo Catch / handle OOM? */
344
345 return VINF_SUCCESS;
346}
347
348/**
349 * Retrieves information about the next message in the queue.
350 *
351 * @returns IPRT status code. VERR_NO_DATA if no next message is available.
352 * @param pState Pointer to the client's state structure to get message info for.
353 * @param puType Where to store the message type.
354 * @param pcParms Where to store the message parameter count.
355 */
356int vboxSvcClipboardMsgGetNextInfo(PVBOXCLIPBOARDCLIENTSTATE pState, uint32_t *puType, uint32_t *pcParms)
357{
358 AssertPtrReturn(puType, VERR_INVALID_POINTER);
359 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
360
361 int rc;
362
363 if (pState->queueMsg.isEmpty())
364 {
365 rc = VERR_NO_DATA;
366 }
367 else
368 {
369 PVBOXCLIPBOARDCLIENTMSG pMsg = pState->queueMsg.first();
370 AssertPtr(pMsg);
371
372 *puType = pMsg->m_uMsg;
373 *pcParms = pMsg->m_cParms;
374
375 rc = VINF_SUCCESS;
376 }
377
378 LogFlowFunc(("Returning puMsg=%RU32, pcParms=%RU32, rc=%Rrc\n", *puType, *pcParms, rc));
379 return rc;
380}
381
382/**
383 * Retrieves the next queued up message and removes it from the queue on success.
384 * Will return VERR_NO_DATA if no next message is available.
385 *
386 * @returns IPRT status code.
387 * @param pState Pointer to the client's state structure to get message for.
388 * @param uMsg Message type to retrieve.
389 * @param cParms Number of parameters the \@a paParms array can store.
390 * @param paParms Where to store the message parameters.
391 */
392int vboxSvcClipboardMsgGetNext(PVBOXCLIPBOARDCLIENTSTATE pState,
393 uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
394{
395 LogFlowFunc(("uMsg=%RU32, cParms=%RU32\n", uMsg, cParms));
396
397 /* Check for pending messages in our queue. */
398 if (pState->queueMsg.isEmpty())
399 return VERR_NO_DATA;
400
401 /* Get the current message. */
402 PVBOXCLIPBOARDCLIENTMSG pMsg = pState->queueMsg.first();
403 AssertPtr(pMsg);
404
405 int rc = VINF_SUCCESS;
406
407 /* Fetch the current message info. */
408 if (pMsg->m_uMsg != uMsg)
409 {
410 LogFunc(("Stored message type (%RU32) does not match request (%RU32)\n", pMsg->m_uMsg, uMsg));
411 rc = VERR_INVALID_PARAMETER;
412 }
413 else if (pMsg->m_cParms > cParms)
414 {
415 LogFunc(("Stored parameter count (%RU32) exceeds request buffer (%RU32)\n", pMsg->m_cParms, cParms));
416 rc = VERR_INVALID_PARAMETER;
417 }
418
419 if (RT_SUCCESS(rc))
420 {
421 rc = Message::CopyParms(paParms, cParms, pMsg->m_paParms, pMsg->m_cParms, true /* fDeepCopy */);
422
423 /** @todo Only remove on success? */
424 pState->queueMsg.removeFirst(); /* Remove the current message from the queue. */
425 }
426 else
427 {
428 vboxSvcClipboardMsgQueueReset(pState);
429 /** @todo Cleanup, send notification to guest. */
430 }
431
432 LogFlowFunc(("Message processed with rc=%Rrc\n", rc));
433 return rc;
434}
435
436/* Set the HGCM parameters according to pending messages.
437 * Executed under the clipboard lock.
438 */
439static bool vboxSvcClipboardReturnMsg(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
440{
441 /** @todo r=andy The client at the moment supplies two parameters, which we can
442 * use by filling in the next message type sent by the host service.
443 * Make this more flexible later, as I don't want to break the existing protocol right now. */
444 if (cParms < 2)
445 {
446 AssertFailed(); /* Should never happen. */
447 return false;
448 }
449
450 /* Message priority is taken into account. */
451 if (pClientData->State.fHostMsgQuit)
452 {
453 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
454 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
455 HGCMSvcSetU32(&paParms[1], 0);
456 pClientData->State.fHostMsgQuit = false;
457 }
458 else if (pClientData->State.fHostMsgReadData)
459 {
460 uint32_t fFormat = 0;
461
462 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: u32RequestedFormat=%02X\n",
463 pClientData->State.u32RequestedFormat));
464
465 if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
466 fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
467 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
468 fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
469 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
470 fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
471#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
472 else if (pClientData->State.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
473 fFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
474#endif
475 else
476 {
477 LogRel2(("Clipboard: Unsupported format from guest (0x%x), skipping\n", fFormat));
478 pClientData->State.u32RequestedFormat = 0;
479 }
480 pClientData->State.u32RequestedFormat &= ~fFormat;
481 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
482 HGCMSvcSetU32(&paParms[1], fFormat);
483 if (pClientData->State.u32RequestedFormat == 0)
484 pClientData->State.fHostMsgReadData = false;
485 }
486 else if (pClientData->State.fHostMsgFormats)
487 {
488 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS: u32AvailableFormats=%02X\n",
489 pClientData->State.u32AvailableFormats));
490
491 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS);
492 HGCMSvcSetU32(&paParms[1], pClientData->State.u32AvailableFormats);
493 pClientData->State.fHostMsgFormats = false;
494 }
495#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
496 else if (vboxSvcClipboardURIReturnMsg(pClientData, cParms, paParms))
497 {
498 /* Nothing to do here yet. */
499 }
500#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
501 else
502 {
503 /* No pending messages. */
504 LogFlowFunc(("No pending message\n"));
505 return false;
506 }
507
508 /* Message information assigned. */
509 return true;
510}
511
512int vboxSvcClipboardReportMsg(PVBOXCLIPBOARDCLIENTDATA pClientData, uint32_t uMsg, uint32_t uFormats)
513{
514 AssertPtrReturn(pClientData, VERR_INVALID_POINTER);
515
516 int rc = VINF_SUCCESS;
517
518 LogFlowFunc(("uMsg=%RU32, fIsAsync=%RTbool\n", uMsg, pClientData->State.fAsync));
519
520 if (VBoxSvcClipboardLock())
521 {
522 switch (uMsg)
523 {
524 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
525 {
526 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
527 pClientData->State.fHostMsgQuit = true;
528 } break;
529
530 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
531 {
532 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
533 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
534 {
535 /* Skip the message. */
536 break;
537 }
538
539 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: uFormats=%02X\n", uFormats));
540 pClientData->State.u32RequestedFormat = uFormats;
541 pClientData->State.fHostMsgReadData = true;
542 } break;
543
544 case VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS:
545 {
546 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
547 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
548 {
549 /* Skip the message. */
550 break;
551 }
552
553 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS: uFormats=%02X\n", uFormats));
554 pClientData->State.u32AvailableFormats = uFormats;
555 pClientData->State.fHostMsgFormats = true;
556 } break;
557
558 default:
559 {
560#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
561 rc = vboxSvcClipboardURIReportMsg(pClientData, uMsg, uFormats);
562#else
563 AssertMsgFailed(("Invalid message %RU32\n", uMsg));
564 rc = VERR_INVALID_PARAMETER;
565#endif
566 } break;
567 }
568
569 if (RT_SUCCESS(rc))
570 {
571 if (pClientData->State.fAsync)
572 {
573 /* The client waits for a response. */
574 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClientData,
575 pClientData->State.async.cParms,
576 pClientData->State.async.paParms);
577
578 /* Make a copy of the handle. */
579 VBOXHGCMCALLHANDLE callHandle = pClientData->State.async.callHandle;
580
581 if (fMessageReturned)
582 {
583 /* There is a response. */
584 pClientData->State.fAsync = false;
585 }
586
587 VBoxSvcClipboardUnlock();
588
589 if (fMessageReturned)
590 {
591 LogFlowFunc(("CallComplete\n"));
592 g_pHelpers->pfnCallComplete(callHandle, VINF_SUCCESS);
593 }
594 }
595 else
596 VBoxSvcClipboardUnlock();
597 }
598 else
599 VBoxSvcClipboardUnlock();
600 }
601
602 LogFlowFuncLeaveRC(rc);
603 return rc;
604}
605
606
607int vboxSvcClipboardSetSource(PVBOXCLIPBOARDCLIENTDATA pClientData, SHAREDCLIPBOARDSOURCE enmSource)
608{
609 if (!pClientData) /* If no client connected (anymore), bail out. */
610 return VINF_SUCCESS;
611
612 int rc = VINF_SUCCESS;
613
614 if (VBoxSvcClipboardLock())
615 {
616 pClientData->State.enmSource = enmSource;
617
618 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClientData->State.u32ClientID, pClientData->State.enmSource));
619
620 VBoxSvcClipboardUnlock();
621 }
622
623 LogFlowFuncLeaveRC(rc);
624 return rc;
625}
626
627static int svcInit(void)
628{
629 int rc = RTCritSectInit(&g_CritSect);
630
631 if (RT_SUCCESS(rc))
632 {
633 vboxSvcClipboardModeSet(VBOX_SHARED_CLIPBOARD_MODE_OFF);
634
635 rc = VBoxClipboardSvcImplInit();
636
637 /* Clean up on failure, because 'svnUnload' will not be called
638 * if the 'svcInit' returns an error.
639 */
640 if (RT_FAILURE(rc))
641 {
642 RTCritSectDelete(&g_CritSect);
643 }
644 }
645
646 return rc;
647}
648
649static DECLCALLBACK(int) svcUnload(void *)
650{
651 LogFlowFuncEnter();
652
653 VBoxClipboardSvcImplDestroy();
654
655 ClipboardClientMap::iterator itClient = g_mapClients.begin();
656 while (itClient != g_mapClients.end())
657 {
658 RTMemFree(itClient->second);
659 g_mapClients.erase(itClient);
660
661 itClient = g_mapClients.begin();
662 }
663
664 RTCritSectDelete(&g_CritSect);
665
666 return VINF_SUCCESS;
667}
668
669/**
670 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
671 * to the guest side.
672 */
673static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
674{
675 RT_NOREF(u32ClientID);
676
677 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
678
679 ClipboardClientMap::iterator itClient = g_mapClients.find(u32ClientID);
680 if (itClient == g_mapClients.end())
681 {
682 AssertFailed(); /* Should never happen. */
683 return VERR_NOT_FOUND;
684 }
685
686 PVBOXCLIPBOARDCLIENT pClient = itClient->second;
687 AssertPtr(pClient);
688 PVBOXCLIPBOARDCLIENTDATA pClientData = pClient->pData;
689 AssertPtr(pClientData);
690
691 /* Sanity. */
692 Assert(pClientData == (PVBOXCLIPBOARDCLIENTDATA)pvClient);
693
694 vboxSvcClipboardReportMsg(pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0); /** @todo r=andy Why is this necessary? The client already disconnected ... */
695
696 vboxSvcClipboardCompleteReadData(pClientData, VERR_NO_DATA, 0);
697
698#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
699 PSHAREDCLIPBOARDURITRANSFER pTransfer = SharedClipboardURICtxGetTransfer(&pClientData->URI, 0 /* Index*/);
700 if (pTransfer)
701 vboxSvcClipboardURIAreaDetach(&pClientData->State, pTransfer);
702
703 SharedClipboardURICtxDestroy(&pClientData->URI);
704#endif
705
706 VBoxClipboardSvcImplDisconnect(pClientData);
707
708 vboxSvcClipboardClientStateReset(&pClientData->State);
709 vboxSvcClipboardClientStateDestroy(&pClientData->State);
710
711 RTMemFree(itClient->second);
712 g_mapClients.erase(itClient);
713
714 return VINF_SUCCESS;
715}
716
717static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
718{
719 RT_NOREF(fRequestor, fRestoring);
720
721 LogFlowFunc(("u32ClientID=%RU32\n", u32ClientID));
722
723 int rc = VINF_SUCCESS;
724
725 if (g_mapClients.find(u32ClientID) != g_mapClients.end())
726 {
727 rc = VERR_ALREADY_EXISTS;
728 AssertFailed(); /* Should never happen. */
729 }
730
731 if (RT_SUCCESS(rc))
732 {
733 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)RTMemAllocZ(sizeof(VBOXCLIPBOARDCLIENT));
734 if (RT_SUCCESS(rc))
735 {
736 pClient->uClientID = u32ClientID;
737 pClient->pData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
738
739 /* Reset the client state. */
740 vboxSvcClipboardClientStateReset(&pClient->pData->State);
741
742 /* (Re-)initialize the client state. */
743 vboxSvcClipboardClientStateInit(&pClient->pData->State, u32ClientID);
744
745 rc = VBoxClipboardSvcImplConnect(pClient->pData, VBoxSvcClipboardGetHeadless());
746#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
747 if (RT_SUCCESS(rc))
748 rc = SharedClipboardURICtxInit(&pClient->pData->URI);
749#endif
750 g_mapClients[u32ClientID] = pClient; /** @todo Can this throw? */
751 }
752 }
753
754 LogFlowFuncLeaveRC(rc);
755 return rc;
756}
757
758static DECLCALLBACK(void) svcCall(void *,
759 VBOXHGCMCALLHANDLE callHandle,
760 uint32_t u32ClientID,
761 void *pvClient,
762 uint32_t u32Function,
763 uint32_t cParms,
764 VBOXHGCMSVCPARM paParms[],
765 uint64_t tsArrival)
766{
767 RT_NOREF(u32ClientID, pvClient, tsArrival);
768
769 int rc = VINF_SUCCESS;
770
771 LogFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=%p\n",
772 u32ClientID, u32Function, cParms, paParms));
773
774 ClipboardClientMap::iterator itClient = g_mapClients.find(u32ClientID);
775 if (itClient == g_mapClients.end())
776 {
777 AssertFailed(); /* Should never happen. */
778 return;
779 }
780
781 PVBOXCLIPBOARDCLIENT pClient = itClient->second;
782 AssertPtr(pClient);
783 PVBOXCLIPBOARDCLIENTDATA pClientData = pClient->pData;
784 AssertPtr(pClientData);
785
786#ifdef DEBUG
787 uint32_t i;
788
789 for (i = 0; i < cParms; i++)
790 {
791 /** @todo parameters other than 32 bit */
792 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
793 }
794#endif
795
796 bool fDefer = false;
797
798 switch (u32Function)
799 {
800 case VBOX_SHARED_CLIPBOARD_GUEST_FN_GET_HOST_MSG_OLD:
801 {
802 /* The quest requests a host message. */
803 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_GET_HOST_MSG_OLD\n"));
804
805 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG_OLD)
806 {
807 rc = VERR_INVALID_PARAMETER;
808 }
809 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
810 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
811 {
812 rc = VERR_INVALID_PARAMETER;
813 }
814 else
815 {
816 /* Atomically verify the client's state. */
817 if (VBoxSvcClipboardLock())
818 {
819 bool fMessageReturned = vboxSvcClipboardReturnMsg(pClientData, cParms, paParms);
820 if (fMessageReturned)
821 {
822 /* Just return to the caller. */
823 pClientData->State.fAsync = false;
824 }
825 else
826 {
827 /* No event available at the time. Process asynchronously. */
828 fDefer = true;
829
830 pClientData->State.fAsync = true;
831 pClientData->State.async.callHandle = callHandle;
832 pClientData->State.async.cParms = cParms;
833 pClientData->State.async.paParms = paParms;
834 }
835
836 VBoxSvcClipboardUnlock();
837 }
838 else
839 {
840 rc = VERR_NOT_SUPPORTED;
841 }
842 }
843 } break;
844
845 case VBOX_SHARED_CLIPBOARD_GUEST_FN_REPORT_FORMATS:
846 {
847 /* The guest reports that some formats are available. */
848 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_REPORT_FORMATS\n"));
849
850 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_REPORT_FORMATS)
851 {
852 rc = VERR_INVALID_PARAMETER;
853 }
854 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
855 {
856 rc = VERR_INVALID_PARAMETER;
857 }
858 else
859 {
860 uint32_t u32Formats;
861 rc = HGCMSvcGetU32(&paParms[0], &u32Formats);
862 if (RT_SUCCESS(rc))
863 {
864 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
865 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
866 {
867 rc = VERR_ACCESS_DENIED;
868 }
869 else
870 {
871 rc = vboxSvcClipboardSetSource(pClientData, SHAREDCLIPBOARDSOURCE_REMOTE);
872
873#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST_DISABLED
874 if ( RT_SUCCESS(rc)
875 && (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST))
876 {
877 if (!SharedClipboardURICtxTransfersMaximumReached(&pClientData->URI))
878 {
879 SharedClipboardURICtxTransfersCleanup(&pClientData->URI);
880
881 PSHAREDCLIPBOARDURITRANSFER pTransfer;
882 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_READ,
883 SHAREDCLIPBOARDSOURCE_REMOTE, &pTransfer);
884 if (RT_SUCCESS(rc))
885 {
886 rc = vboxSvcClipboardURIAreaRegister(&pClientData->State, pTransfer);
887 if (RT_SUCCESS(rc))
888 {
889 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
890 RT_ZERO(creationCtx);
891
892 creationCtx.enmSource = pClientData->State.enmSource;
893
894 RT_ZERO(creationCtx.Interface);
895 RT_ZERO(creationCtx.Interface);
896 creationCtx.Interface.pfnTransferOpen = vboxSvcClipboardURITransferOpen;
897 creationCtx.Interface.pfnTransferClose = vboxSvcClipboardURITransferClose;
898 creationCtx.Interface.pfnListOpen = vboxSvcClipboardURIListOpen;
899 creationCtx.Interface.pfnListClose = vboxSvcClipboardURIListClose;
900 creationCtx.Interface.pfnListHdrRead = vboxSvcClipboardURIListHdrRead;
901 creationCtx.Interface.pfnListEntryRead = vboxSvcClipboardURIListEntryRead;
902 creationCtx.Interface.pfnObjOpen = vboxSvcClipboardURIObjOpen;
903 creationCtx.Interface.pfnObjClose = vboxSvcClipboardURIObjClose;
904 creationCtx.Interface.pfnObjRead = vboxSvcClipboardURIObjRead;
905
906 creationCtx.pvUser = pClient;
907
908 /* Register needed callbacks so that we can wait for the meta data to arrive here. */
909 SHAREDCLIPBOARDURITRANSFERCALLBACKS Callbacks;
910 RT_ZERO(Callbacks);
911
912 Callbacks.pvUser = pClientData;
913
914 Callbacks.pfnTransferPrepare = VBoxSvcClipboardURITransferPrepareCallback;
915 Callbacks.pfnTransferComplete = VBoxSvcClipboardURITransferCompleteCallback;
916 Callbacks.pfnTransferCanceled = VBoxSvcClipboardURITransferCanceledCallback;
917 Callbacks.pfnTransferError = VBoxSvcClipboardURITransferErrorCallback;
918
919 SharedClipboardURITransferSetCallbacks(pTransfer, &Callbacks);
920
921 rc = SharedClipboardURITransferProviderCreate(pTransfer, &creationCtx);
922 if (RT_SUCCESS(rc))
923 rc = SharedClipboardURICtxTransferAdd(&pClientData->URI, pTransfer);
924 }
925
926 if (RT_SUCCESS(rc))
927 {
928 rc = VBoxClipboardSvcImplURITransferCreate(pClientData, pTransfer);
929 }
930 else
931 {
932 VBoxClipboardSvcImplURITransferDestroy(pClientData, pTransfer);
933 SharedClipboardURITransferDestroy(pTransfer);
934 }
935 }
936 }
937 else
938 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
939
940 LogFunc(("VBOX_SHARED_CLIPBOARD_FMT_URI_LIST: %Rrc\n", rc));
941
942 if (RT_FAILURE(rc))
943 LogRel(("Shared Clipboard: Initializing URI guest to host read transfer failed with %Rrc\n", rc));
944 }
945#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
946
947 if (RT_SUCCESS(rc))
948 {
949 if (u32Formats & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
950 {
951 /* Tell the guest that we want to start a (reading) transfer. */
952 rc = vboxSvcClipboardReportMsg(pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_URI_TRANSFER_START,
953 0 /* u32Formats == 0 means reading data */);
954
955 /* Note: Announcing the actual format will be done in the
956 host service URI handler (vboxSvcClipboardURIHandler). */
957 }
958 else /* Announce simple formats to the OS-specific service implemenation. */
959 {
960 if (g_pfnExtension)
961 {
962 VBOXCLIPBOARDEXTPARMS parms;
963 RT_ZERO(parms);
964 parms.u32Format = u32Formats;
965
966 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof (parms));
967 }
968
969 rc = VBoxClipboardSvcImplFormatAnnounce(pClientData, u32Formats);
970 }
971 }
972 }
973 }
974 }
975 } break;
976
977 case VBOX_SHARED_CLIPBOARD_GUEST_FN_READ_DATA:
978 {
979 /* The guest wants to read data in the given format. */
980 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_READ_DATA\n"));
981
982 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
983 {
984 rc = VERR_INVALID_PARAMETER;
985 }
986 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
987 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
988 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
989 )
990 {
991 rc = VERR_INVALID_PARAMETER;
992 }
993 else
994 {
995 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
996 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
997 {
998 rc = VERR_ACCESS_DENIED;
999 break;
1000 }
1001
1002 uint32_t u32Format;
1003 rc = HGCMSvcGetU32(&paParms[0], &u32Format);
1004 if (RT_SUCCESS(rc))
1005 {
1006#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1007 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
1008 {
1009 if (!SharedClipboardURICtxTransfersMaximumReached(&pClientData->URI))
1010 {
1011 SharedClipboardURICtxTransfersCleanup(&pClientData->URI);
1012
1013 PSHAREDCLIPBOARDURITRANSFER pTransfer;
1014 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_WRITE,
1015 pClientData->State.enmSource,
1016 &pTransfer);
1017 if (RT_SUCCESS(rc))
1018 {
1019 /* Attach to the most recent clipboard area. */
1020 rc = vboxSvcClipboardURIAreaAttach(&pClientData->State, pTransfer, 0 /* Area ID */);
1021 if (RT_SUCCESS(rc))
1022 {
1023 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
1024 RT_ZERO(creationCtx);
1025
1026 creationCtx.enmSource = SharedClipboardURITransferGetSource(pTransfer);
1027
1028 RT_ZERO(creationCtx.Interface);
1029
1030 creationCtx.Interface.pfnListHdrWrite = vboxSvcClipboardURIListHdrWrite;
1031 creationCtx.Interface.pfnListEntryWrite = vboxSvcClipboardURIListEntryWrite;
1032 creationCtx.Interface.pfnObjWrite = vboxSvcClipboardURIObjWrite;
1033
1034 creationCtx.pvUser = pClientData;
1035
1036 rc = SharedClipboardURITransferProviderCreate(pTransfer, &creationCtx);
1037 if (RT_SUCCESS(rc))
1038 rc = SharedClipboardURICtxTransferAdd(&pClientData->URI, pTransfer);
1039 }
1040
1041 if (RT_SUCCESS(rc))
1042 {
1043 rc = VBoxClipboardSvcImplURITransferCreate(pClientData, pTransfer);
1044 }
1045 else
1046 {
1047 VBoxClipboardSvcImplURITransferDestroy(pClientData, pTransfer);
1048 SharedClipboardURITransferDestroy(pTransfer);
1049 }
1050 }
1051 }
1052 else
1053 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
1054
1055 if (RT_FAILURE(rc))
1056 LogRel(("Shared Clipboard: Initializing URI host to guest write transfer failed with %Rrc\n", rc));
1057 }
1058 else
1059 {
1060#endif
1061 void *pv;
1062 uint32_t cb;
1063 rc = VBoxHGCMParmPtrGet(&paParms[1], &pv, &cb);
1064 if (RT_SUCCESS(rc))
1065 {
1066 uint32_t cbActual = 0;
1067
1068 if (g_pfnExtension)
1069 {
1070 VBOXCLIPBOARDEXTPARMS parms;
1071 RT_ZERO(parms);
1072
1073 parms.u32Format = u32Format;
1074 parms.u.pvData = pv;
1075 parms.cbData = cb;
1076
1077 g_fReadingData = true;
1078
1079 rc = g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof (parms));
1080 LogFlowFunc(("DATA: g_fDelayedAnnouncement = %d, g_u32DelayedFormats = 0x%x\n", g_fDelayedAnnouncement, g_u32DelayedFormats));
1081
1082 if (g_fDelayedAnnouncement)
1083 {
1084 vboxSvcClipboardReportMsg(pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, g_u32DelayedFormats);
1085 g_fDelayedAnnouncement = false;
1086 g_u32DelayedFormats = 0;
1087 }
1088
1089 g_fReadingData = false;
1090
1091 if (RT_SUCCESS (rc))
1092 {
1093 cbActual = parms.cbData;
1094 }
1095 }
1096
1097 /* Release any other pending read, as we only
1098 * support one pending read at one time. */
1099 rc = vboxSvcClipboardCompleteReadData(pClientData, VERR_NO_DATA, 0);
1100 if (RT_SUCCESS(rc))
1101 rc = VBoxClipboardSvcImplReadData(pClientData, u32Format, pv, cb, &cbActual);
1102
1103 /* Remember our read request until it is completed.
1104 * See the protocol description above for more
1105 * information. */
1106 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1107 {
1108 if (VBoxSvcClipboardLock())
1109 {
1110 pClientData->State.asyncRead.callHandle = callHandle;
1111 pClientData->State.asyncRead.cParms = cParms;
1112 pClientData->State.asyncRead.paParms = paParms;
1113 pClientData->State.fReadPending = true;
1114 fDefer = true;
1115 VBoxSvcClipboardUnlock();
1116 }
1117 else
1118 rc = VERR_NOT_SUPPORTED;
1119 }
1120 else if (RT_SUCCESS (rc))
1121 {
1122 HGCMSvcSetU32(&paParms[2], cbActual);
1123 }
1124 }
1125#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1126 }
1127#endif
1128 }
1129 }
1130 } break;
1131
1132 case VBOX_SHARED_CLIPBOARD_GUEST_FN_WRITE_DATA:
1133 {
1134 /* The guest writes the requested data. */
1135 LogFunc(("VBOX_SHARED_CLIPBOARD_GUEST_FN_WRITE_DATA\n"));
1136
1137 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
1138 {
1139 rc = VERR_INVALID_PARAMETER;
1140 }
1141 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
1142 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
1143 )
1144 {
1145 rc = VERR_INVALID_PARAMETER;
1146 }
1147 else
1148 {
1149 void *pv;
1150 uint32_t cb;
1151 uint32_t u32Format;
1152
1153 rc = HGCMSvcGetU32(&paParms[0], &u32Format);
1154 if (RT_SUCCESS(rc))
1155 {
1156 rc = VBoxHGCMParmPtrGet(&paParms[1], &pv, &cb);
1157 if (RT_SUCCESS(rc))
1158 {
1159 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
1160 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
1161 {
1162 rc = VERR_NOT_SUPPORTED;
1163 break;
1164 }
1165
1166 if (g_pfnExtension)
1167 {
1168 VBOXCLIPBOARDEXTPARMS parms;
1169 RT_ZERO(parms);
1170
1171 parms.u32Format = u32Format;
1172 parms.u.pvData = pv;
1173 parms.cbData = cb;
1174
1175 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof (parms));
1176 }
1177
1178 rc = VBoxClipboardSvcImplWriteData(pClientData, pv, cb, u32Format);
1179 }
1180 }
1181 }
1182 } break;
1183
1184 default:
1185 {
1186#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1187 rc = vboxSvcClipboardURIHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
1188 if (RT_SUCCESS(rc))
1189 {
1190 /* The URI handler does deferring on its own. */
1191 fDefer = true;
1192 }
1193#else
1194 rc = VERR_NOT_IMPLEMENTED;
1195#endif
1196 } break;
1197 }
1198
1199 LogFlowFunc(("u32ClientID=%RU32, fDefer=%RTbool\n", pClient->uClientID, fDefer));
1200
1201 if (!fDefer)
1202 g_pHelpers->pfnCallComplete(callHandle, rc);
1203
1204 LogFlowFuncLeaveRC(rc);
1205}
1206
1207/** If the client in the guest is waiting for a read operation to complete
1208 * then complete it, otherwise return. See the protocol description in the
1209 * shared clipboard module description. */
1210int vboxSvcClipboardCompleteReadData(PVBOXCLIPBOARDCLIENTDATA pClientData, int rc, uint32_t cbActual)
1211{
1212 VBOXHGCMCALLHANDLE callHandle = NULL;
1213 VBOXHGCMSVCPARM *paParms = NULL;
1214 bool fReadPending = false;
1215 if (VBoxSvcClipboardLock()) /* if not can we do anything useful? */
1216 {
1217 callHandle = pClientData->State.asyncRead.callHandle;
1218 paParms = pClientData->State.asyncRead.paParms;
1219 fReadPending = pClientData->State.fReadPending;
1220 pClientData->State.fReadPending = false;
1221 VBoxSvcClipboardUnlock();
1222 }
1223 if (fReadPending)
1224 {
1225 HGCMSvcSetU32(&paParms[2], cbActual);
1226 g_pHelpers->pfnCallComplete(callHandle, rc);
1227 }
1228
1229 return VINF_SUCCESS;
1230}
1231
1232/**
1233 * Defers a client from returning back to the caller (guest side).
1234 *
1235 * @returns VBox status code.
1236 * @param pClient Client to defer.
1237 * @param hHandle The call handle to defer.
1238 * @param u32Function Function ID to save.
1239 * @param cParms Number of parameters to save.
1240 * @param paParms Parameter arrray to save.
1241 */
1242int vboxSvcClipboardClientDefer(PVBOXCLIPBOARDCLIENT pClient,
1243 VBOXHGCMCALLHANDLE hHandle, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1244{
1245 LogFlowFunc(("uClient=%RU32\n", pClient->uClientID));
1246
1247 AssertMsgReturn(pClient->fDeferred == false, ("Client already in deferred mode\n"),
1248 VERR_WRONG_ORDER);
1249
1250 pClient->fDeferred = true;
1251
1252 pClient->Deferred.hHandle = hHandle;
1253 pClient->Deferred.uType = u32Function;
1254 pClient->Deferred.cParms = cParms;
1255 pClient->Deferred.paParms = paParms;
1256
1257 return false;
1258}
1259
1260/**
1261 * Completes a call of a client, which in turn will return the result to the caller on
1262 * the guest side.
1263 *
1264 * @returns VBox status code.
1265 * @param pClient Client to complete.
1266 * @param hHandle Call handle to complete.
1267 * @param rc Return code to set for the client.
1268 */
1269int vboxSvcClipboardClientComplete(PVBOXCLIPBOARDCLIENT pClient, VBOXHGCMCALLHANDLE hHandle, int rc)
1270{
1271 RT_NOREF(pClient);
1272
1273 LogFlowFunc(("uClient=%RU32, rc=%Rrc\n", pClient->uClientID, rc));
1274
1275 g_pHelpers->pfnCallComplete(hHandle, rc);
1276
1277 return VINF_SUCCESS;
1278}
1279
1280/**
1281 * Completes a deferred client.
1282 *
1283 * @returns VBox status code.
1284 * @param pClient Client to complete.
1285 * @param rcComplete Return code to set for the client.
1286 */
1287int vboxSvcClipboardClientDeferredComplete(PVBOXCLIPBOARDCLIENT pClient, int rcComplete)
1288{
1289 LogFlowFunc(("uClient=%RU32, fDeferred=%RTbool\n", pClient->uClientID, pClient->fDeferred));
1290
1291 int rc = VINF_SUCCESS;
1292
1293 if (pClient->fDeferred) /* Not deferred? Bail out early. */
1294 {
1295 LogFlowFunc(("Completing call\n"));
1296
1297 rc = vboxSvcClipboardClientComplete(pClient, pClient->Deferred.hHandle, rcComplete);
1298
1299 pClient->fDeferred = false;
1300 RT_ZERO(pClient->Deferred);
1301 }
1302
1303 return rc;
1304}
1305
1306/**
1307 * Sets a deferred client's next message info -- when returning to the client, it then
1308 * can retrieve the actual message sent by the host.
1309 *
1310 * @returns VBox status code.
1311 * @param pClient Client to set the next message information for.
1312 * @param uMsg Message ID to set.
1313 * @param cParms Number of parameters of message required.
1314 */
1315int vboxSvcClipboardClientDeferredSetMsgInfo(PVBOXCLIPBOARDCLIENT pClient, uint32_t uMsg, uint32_t cParms)
1316{
1317 int rc;
1318
1319 LogFlowFunc(("uClient=%RU32\n", pClient->uClientID));
1320
1321 if (pClient->fDeferred)
1322 {
1323 if (pClient->Deferred.cParms >= 2)
1324 {
1325 AssertPtrReturn(pClient->Deferred.paParms, VERR_BUFFER_OVERFLOW);
1326
1327 HGCMSvcSetU32(&pClient->Deferred.paParms[0], uMsg);
1328 HGCMSvcSetU32(&pClient->Deferred.paParms[1], cParms);
1329
1330 rc = VINF_SUCCESS;
1331 }
1332 else
1333 rc = VERR_INVALID_PARAMETER;
1334 }
1335 else
1336 rc = VERR_INVALID_STATE;
1337
1338 LogFlowFuncLeaveRC(rc);
1339 return rc;
1340}
1341
1342/**
1343 * Initializes a Shared Clipboard service's client state.
1344 *
1345 * @returns VBox status code.
1346 * @param pState Client state to initialize.
1347 * @param uClientID Client ID (HGCM) to use for this client state.
1348 */
1349static int vboxSvcClipboardClientStateInit(PVBOXCLIPBOARDCLIENTSTATE pState, uint32_t uClientID)
1350{
1351 LogFlowFuncEnter();
1352
1353 /* Register the client.
1354 * Note: Do *not* memset the struct, as it contains classes (for caching). */
1355 pState->u32ClientID = uClientID;
1356
1357 return VINF_SUCCESS;
1358}
1359
1360/**
1361 * Destroys a Shared Clipboard service's client state.
1362 *
1363 * @returns VBox status code.
1364 * @param pState Client state to destroy.
1365 */
1366static int vboxSvcClipboardClientStateDestroy(PVBOXCLIPBOARDCLIENTSTATE pState)
1367{
1368 LogFlowFuncEnter();
1369
1370 vboxSvcClipboardMsgQueueReset(pState);
1371
1372 return VINF_SUCCESS;
1373}
1374
1375/**
1376 * Resets a Shared Clipboard service's client state.
1377 *
1378 * @param pState Client state to reset.
1379 */
1380static void vboxSvcClipboardClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pState)
1381{
1382 LogFlowFuncEnter();
1383
1384 /** @todo Clear async / asynRead / ... data? */
1385 vboxSvcClipboardMsgQueueReset(pState);
1386
1387 pState->u32ClientID = 0;
1388 pState->fAsync = false;
1389 pState->fReadPending = false;
1390
1391 pState->fHostMsgQuit = false;
1392 pState->fHostMsgReadData = false;
1393 pState->fHostMsgFormats = false;
1394#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1395 pState->URI.fTransferStart = false;
1396 pState->URI.enmTransferDir = SHAREDCLIPBOARDURITRANSFERDIR_UNKNOWN;
1397#endif
1398
1399 pState->u32AvailableFormats = 0;
1400 pState->u32RequestedFormat = 0;
1401}
1402
1403/*
1404 * We differentiate between a function handler for the guest and one for the host.
1405 */
1406static DECLCALLBACK(int) svcHostCall(void *,
1407 uint32_t u32Function,
1408 uint32_t cParms,
1409 VBOXHGCMSVCPARM paParms[])
1410{
1411 int rc = VINF_SUCCESS;
1412
1413 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, paParms=%p\n", u32Function, cParms, paParms));
1414
1415 switch (u32Function)
1416 {
1417 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
1418 {
1419 LogFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE\n"));
1420
1421 if (cParms != 1)
1422 {
1423 rc = VERR_INVALID_PARAMETER;
1424 }
1425 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* mode */
1426 )
1427 {
1428 rc = VERR_INVALID_PARAMETER;
1429 }
1430 else
1431 {
1432 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
1433
1434 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
1435
1436 /* The setter takes care of invalid values. */
1437 vboxSvcClipboardModeSet(u32Mode);
1438 }
1439 } break;
1440
1441 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
1442 {
1443 uint32_t u32Headless = g_fHeadless;
1444
1445 rc = VERR_INVALID_PARAMETER;
1446 if (cParms != 1)
1447 break;
1448
1449 rc = HGCMSvcGetU32(&paParms[0], &u32Headless);
1450 if (RT_SUCCESS(rc))
1451 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS, u32Headless=%u\n",
1452 (unsigned) u32Headless));
1453
1454 g_fHeadless = RT_BOOL(u32Headless);
1455
1456 } break;
1457
1458 default:
1459 {
1460#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1461 rc = vboxSvcClipboardURIHostHandler(u32Function, cParms, paParms);
1462#else
1463 rc = VERR_NOT_IMPLEMENTED;
1464#endif
1465 } break;
1466 }
1467
1468 LogFlowFuncLeaveRC(rc);
1469 return rc;
1470}
1471
1472#ifndef UNIT_TEST
1473/**
1474 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
1475 */
1476static SSMFIELD const g_aClipboardClientDataFields[] =
1477{
1478 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, u32ClientID), /* for validation purposes */
1479 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgQuit),
1480 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgReadData),
1481 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, fHostMsgFormats),
1482 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, u32RequestedFormat),
1483 SSMFIELD_ENTRY_TERM()
1484};
1485#endif
1486
1487static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
1488{
1489 RT_NOREF(u32ClientID);
1490
1491#ifndef UNIT_TEST
1492 /*
1493 * When the state will be restored, pending requests will be reissued
1494 * by VMMDev. The service therefore must save state as if there were no
1495 * pending request.
1496 * Pending requests, if any, will be completed in svcDisconnect.
1497 */
1498 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1499
1500 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
1501
1502 /* This field used to be the length. We're using it as a version field
1503 with the high bit set. */
1504 SSMR3PutU32(pSSM, UINT32_C(0x80000002));
1505 int rc = SSMR3PutStructEx(pSSM, pClientData, sizeof(*pClientData), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1506 AssertRCReturn(rc, rc);
1507
1508#else /* UNIT_TEST */
1509 RT_NOREF3(u32ClientID, pvClient, pSSM);
1510#endif /* UNIT_TEST */
1511 return VINF_SUCCESS;
1512}
1513
1514static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
1515{
1516#ifndef UNIT_TEST
1517 RT_NOREF(u32ClientID, uVersion);
1518
1519 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1520
1521 PVBOXCLIPBOARDCLIENTDATA pClientData = (PVBOXCLIPBOARDCLIENTDATA)pvClient;
1522 AssertPtr(pClientData);
1523
1524 /* Existing client can not be in async state yet. */
1525 Assert(!pClientData->State.fAsync);
1526
1527 /* Save the client ID for data validation. */
1528 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
1529 uint32_t const u32ClientIDOld = pClientData->State.u32ClientID;
1530
1531 /* Restore the client data. */
1532 uint32_t lenOrVer;
1533 int rc = SSMR3GetU32(pSSM, &lenOrVer);
1534 AssertRCReturn(rc, rc);
1535 if (lenOrVer == UINT32_C(0x80000002))
1536 {
1537 rc = SSMR3GetStructEx(pSSM, pClientData, sizeof(*pClientData), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1538 AssertRCReturn(rc, rc);
1539 }
1540 else
1541 {
1542 LogFunc(("Client data size mismatch: got %#x\n", lenOrVer));
1543 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1544 }
1545
1546 /* Verify the client ID. */
1547 if (pClientData->State.u32ClientID != u32ClientIDOld)
1548 {
1549 LogFunc(("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClientData->State.u32ClientID));
1550 pClientData->State.u32ClientID = u32ClientIDOld;
1551 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1552 }
1553
1554 /* Actual host data are to be reported to guest (SYNC). */
1555 VBoxClipboardSvcImplSync(pClientData);
1556
1557#else /* UNIT_TEST*/
1558 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
1559#endif /* UNIT_TEST */
1560 return VINF_SUCCESS;
1561}
1562
1563static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
1564{
1565 RT_NOREF(pvData, cbData);
1566
1567 LogFlowFunc(("u32Function=%RU32\n", u32Function));
1568
1569 int rc = VINF_SUCCESS;
1570
1571 PVBOXCLIPBOARDCLIENTDATA pClientData = NULL; /** @todo FIX !!! */
1572
1573 if (pClientData != NULL)
1574 {
1575 switch (u32Function)
1576 {
1577 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
1578 {
1579 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_fReadingData=%RTbool\n", g_fReadingData));
1580 if (g_fReadingData)
1581 {
1582 g_fDelayedAnnouncement = true;
1583 g_u32DelayedFormats = u32Format;
1584 }
1585 #if 0
1586 else
1587 {
1588 vboxSvcClipboardReportMsg(g_pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS, u32Format);
1589 }
1590 #endif
1591 } break;
1592
1593#if 0
1594 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
1595 {
1596 vboxSvcClipboardReportMsg(g_pClientData, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
1597 } break;
1598#endif
1599
1600 default:
1601 /* Just skip other messages. */
1602 break;
1603 }
1604 }
1605
1606 LogFlowFuncLeaveRC(rc);
1607 return rc;
1608}
1609
1610static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
1611{
1612 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
1613
1614 VBOXCLIPBOARDEXTPARMS parms;
1615 RT_ZERO(parms);
1616
1617 if (pfnExtension)
1618 {
1619 /* Install extension. */
1620 g_pfnExtension = pfnExtension;
1621 g_pvExtension = pvExtension;
1622
1623 parms.u.pfnCallback = extCallback;
1624 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1625 }
1626 else
1627 {
1628 if (g_pfnExtension)
1629 g_pfnExtension(g_pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
1630
1631 /* Uninstall extension. */
1632 g_pfnExtension = NULL;
1633 g_pvExtension = NULL;
1634 }
1635
1636 return VINF_SUCCESS;
1637}
1638
1639extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
1640{
1641 int rc = VINF_SUCCESS;
1642
1643 LogFlowFunc(("ptable=%p\n", ptable));
1644
1645 if (!ptable)
1646 {
1647 rc = VERR_INVALID_PARAMETER;
1648 }
1649 else
1650 {
1651 LogFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
1652
1653 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
1654 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
1655 {
1656 rc = VERR_INVALID_PARAMETER;
1657 }
1658 else
1659 {
1660 g_pHelpers = ptable->pHelpers;
1661
1662 ptable->cbClient = sizeof(VBOXCLIPBOARDCLIENTDATA);
1663
1664 ptable->pfnUnload = svcUnload;
1665 ptable->pfnConnect = svcConnect;
1666 ptable->pfnDisconnect = svcDisconnect;
1667 ptable->pfnCall = svcCall;
1668 ptable->pfnHostCall = svcHostCall;
1669 ptable->pfnSaveState = svcSaveState;
1670 ptable->pfnLoadState = svcLoadState;
1671 ptable->pfnRegisterExtension = svcRegisterExtension;
1672 ptable->pfnNotify = NULL;
1673 ptable->pvService = NULL;
1674
1675 /* Service specific initialization. */
1676 rc = svcInit();
1677 }
1678 }
1679
1680 return rc;
1681}
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