VirtualBox

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

Last change on this file since 80552 was 80552, checked in by vboxsync, 5 years ago

Shared Clipboard: Formatting, SCM fix.

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