VirtualBox

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

最後變更 在這個檔案從80334是 80283,由 vboxsync 提交於 6 年 前

Shared Clipboard/URI: Update; more work on root list handling.

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

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