VirtualBox

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

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

Shared Clipboard: Removed old protocol handling cruft on the host side and added message translation support for serving older Guest Additions. Updated docs / protocol changelog.

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