VirtualBox

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

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

Shared Clipboard/URI: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.6 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 80562 2019-09-03 09:49:42Z 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
246/** Is the clipboard running in headless mode? */
247static bool g_fHeadless = false;
248
249/** Holds the service extension state. */
250VBOXCLIPBOARDEXTSTATE g_ExtState = { 0 };
251
252/** Global map of all connected clients. */
253ClipboardClientMap g_mapClients;
254
255/** Global map of all registered event sources. */
256ClipboardEventSourceMap g_mapEventSources;
257
258/** Global list of all clients which are queued up (deferred return) and ready
259 * to process new commands. The key is the (unique) client ID. */
260ClipboardClientQueue g_listClientsDeferred;
261
262
263/**
264 * Creates a (unique) event source ID.
265 *
266 * @returns VBox status code, or VERR_NOT_FOUND on error.
267 * @param puID Where to store the created event source ID on success.
268 */
269int vboxSvcClipboardEventSourceCreateID(PVBOXCLIPBOARDEVENTSOURCEID puID)
270{
271 AssertPtrReturn(puID, VERR_INVALID_POINTER);
272
273 for (uint32_t i = 0; i < 32; i++) /* Don't try too hard. */
274 {
275 VBOXCLIPBOARDEVENTSOURCEID uID = RTRandU32() % VBOX_SHARED_CLIPBOARD_MAX_EVENT_SOURCES;
276 if (g_mapEventSources.find(uID) == g_mapEventSources.end())
277 {
278 *puID = uID;
279 return VINF_SUCCESS;
280 }
281 }
282
283 return VERR_NOT_FOUND;
284}
285
286uint32_t vboxSvcClipboardGetMode(void)
287{
288 return g_uMode;
289}
290
291#ifdef UNIT_TEST
292/** Testing interface, getter for clipboard mode */
293uint32_t TestClipSvcGetMode(void)
294{
295 return vboxSvcClipboardGetMode();
296}
297#endif
298
299/** Getter for headless setting. Also needed by testcase. */
300bool VBoxSvcClipboardGetHeadless(void)
301{
302 return g_fHeadless;
303}
304
305static int vboxSvcClipboardModeSet(uint32_t uMode)
306{
307 int rc = VERR_NOT_SUPPORTED;
308
309 switch (uMode)
310 {
311 case VBOX_SHARED_CLIPBOARD_MODE_OFF:
312 RT_FALL_THROUGH();
313 case VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST:
314 RT_FALL_THROUGH();
315 case VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST:
316 RT_FALL_THROUGH();
317 case VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL:
318 {
319 g_uMode = uMode;
320
321 rc = VINF_SUCCESS;
322 break;
323 }
324
325 default:
326 {
327 g_uMode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
328 break;
329 }
330 }
331
332 LogFlowFuncLeaveRC(rc);
333 return rc;
334}
335
336bool VBoxSvcClipboardLock(void)
337{
338 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
339}
340
341void VBoxSvcClipboardUnlock(void)
342{
343 int rc2 = RTCritSectLeave(&g_CritSect);
344 AssertRC(rc2);
345}
346
347/**
348 * Resets a client's state message queue.
349 *
350 * @param pClient Pointer to the client data structure to reset message queue for.
351 */
352void vboxSvcClipboardMsgQueueReset(PVBOXCLIPBOARDCLIENT pClient)
353{
354 LogFlowFuncEnter();
355
356 while (!pClient->queueMsg.isEmpty())
357 {
358 RTMemFree(pClient->queueMsg.last());
359 pClient->queueMsg.removeLast();
360 }
361}
362
363/**
364 * Allocates a new clipboard message.
365 *
366 * @returns Allocated clipboard message, or NULL on failure.
367 * @param uMsg Message type of message to allocate.
368 * @param cParms Number of HGCM parameters to allocate.
369 */
370PVBOXCLIPBOARDCLIENTMSG vboxSvcClipboardMsgAlloc(uint32_t uMsg, uint32_t cParms)
371{
372 PVBOXCLIPBOARDCLIENTMSG pMsg = (PVBOXCLIPBOARDCLIENTMSG)RTMemAlloc(sizeof(VBOXCLIPBOARDCLIENTMSG));
373 if (pMsg)
374 {
375 pMsg->m_paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * cParms);
376 if (pMsg->m_paParms)
377 {
378 pMsg->m_cParms = cParms;
379 pMsg->m_uMsg = uMsg;
380
381 return pMsg;
382 }
383 }
384
385 RTMemFree(pMsg);
386 return NULL;
387}
388
389/**
390 * Frees a formerly allocated clipboard message.
391 *
392 * @param pMsg Clipboard message to free.
393 * The pointer will be invalid after calling this function.
394 */
395void vboxSvcClipboardMsgFree(PVBOXCLIPBOARDCLIENTMSG pMsg)
396{
397 if (!pMsg)
398 return;
399
400 if (pMsg->m_paParms)
401 RTMemFree(pMsg->m_paParms);
402
403 RTMemFree(pMsg);
404 pMsg = NULL;
405}
406
407/**
408 * Sets the VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_NOWAIT
409 * return parameters.
410 *
411 * @param pMsg Message to set return parameters to.
412 * @param paDstParms The peek parameter vector.
413 * @param cDstParms The number of peek parameters (at least two).
414 * @remarks ASSUMES the parameters has been cleared by clientMsgPeek.
415 */
416void vboxSvcClipboardMsgSetPeekReturn(PVBOXCLIPBOARDCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
417{
418 Assert(cDstParms >= 2);
419 if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
420 paDstParms[0].u.uint32 = pMsg->m_uMsg;
421 else
422 paDstParms[0].u.uint64 = pMsg->m_uMsg;
423 paDstParms[1].u.uint32 = pMsg->m_cParms;
424
425 uint32_t i = RT_MIN(cDstParms, pMsg->m_cParms + 2);
426 while (i-- > 2)
427 switch (pMsg->m_paParms[i - 2].type)
428 {
429 case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
430 case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
431 case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = pMsg->m_paParms[i - 2].u.pointer.size; break;
432 }
433}
434
435/**
436 * Adds a new message to a client'S message queue.
437 *
438 * @returns IPRT status code.
439 * @param pClient Pointer to the client data structure to add new message to.
440 * @param pMsg Pointer to message to add. The queue then owns the pointer.
441 * @param fAppend Whether to append or prepend the message to the queue.
442 */
443int vboxSvcClipboardMsgAdd(PVBOXCLIPBOARDCLIENT pClient, PVBOXCLIPBOARDCLIENTMSG pMsg, bool fAppend)
444{
445 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
446
447 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32, fAppend=%RTbool\n",
448 pMsg->m_uMsg, VBoxClipboardHostMsgToStr(pMsg->m_uMsg), pMsg->m_cParms, fAppend));
449
450 if (fAppend)
451 pClient->queueMsg.append(pMsg);
452 else
453 pClient->queueMsg.prepend(pMsg);
454
455 /** @todo Catch / handle OOM? */
456
457 return VINF_SUCCESS;
458}
459
460/**
461 * Implements VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_NOWAIT.
462 *
463 * @returns VBox status code.
464 * @retval VINF_SUCCESS if a message was pending and is being returned.
465 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
466 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
467 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
468 *
469 * @param pClient The client state.
470 * @param hCall The client's call handle.
471 * @param cParms Number of parameters.
472 * @param paParms Array of parameters.
473 * @param fWait Set if we should wait for a message, clear if to return
474 * immediately.
475 */
476int vboxSvcClipboardMsgPeek(PVBOXCLIPBOARDCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[],
477 bool fWait)
478{
479 /*
480 * Validate the request.
481 */
482 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
483
484 uint64_t idRestoreCheck = 0;
485 uint32_t i = 0;
486 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
487 {
488 idRestoreCheck = paParms[0].u.uint64;
489 paParms[0].u.uint64 = 0;
490 i++;
491 }
492 for (; i < cParms; i++)
493 {
494 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
495 VERR_WRONG_PARAMETER_TYPE);
496 paParms[i].u.uint32 = 0;
497 }
498
499 /*
500 * Check restore session ID.
501 */
502 if (idRestoreCheck != 0)
503 {
504 uint64_t idRestore = g_pHelpers->pfnGetVMMDevSessionId(g_pHelpers);
505 if (idRestoreCheck != idRestore)
506 {
507 paParms[0].u.uint64 = idRestore;
508 LogFlowFunc(("[Client %RU32] VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_XXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
509 pClient->uClientID, idRestoreCheck, idRestore));
510 return VERR_VM_RESTORED;
511 }
512 Assert(!g_pHelpers->pfnIsCallRestored(hCall));
513 }
514
515 /*
516 * Return information about the first message if one is pending in the list.
517 */
518 if (!pClient->queueMsg.isEmpty())
519 {
520 PVBOXCLIPBOARDCLIENTMSG pFirstMsg = pClient->queueMsg.first();
521 if (pFirstMsg)
522 {
523 vboxSvcClipboardMsgSetPeekReturn(pFirstMsg, paParms, cParms);
524 LogFlowFunc(("[Client %RU32] VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_XXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
525 pClient->uClientID, pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg),
526 pFirstMsg->m_cParms));
527 return VINF_SUCCESS;
528 }
529 }
530
531 /*
532 * If we cannot wait, fail the call.
533 */
534 if (!fWait)
535 {
536 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->uClientID));
537 return VERR_TRY_AGAIN;
538 }
539
540 /*
541 * Wait for the host to queue a message for this client.
542 */
543 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
544 pClient->uClientID), VERR_RESOURCE_BUSY);
545 pClient->Pending.hHandle = hCall;
546 pClient->Pending.cParms = cParms;
547 pClient->Pending.paParms = paParms;
548 pClient->Pending.uType = VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_WAIT;
549 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->uClientID));
550 return VINF_HGCM_ASYNC_EXECUTE;
551}
552
553/**
554 * Implements VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_GET.
555 *
556 * @returns VBox status code.
557 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
558 * @retval VERR_TRY_AGAIN if no message pending.
559 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
560 * size was updated to reflect the required size, though this isn't yet
561 * forwarded to the guest. (The guest is better of using peek with
562 * parameter count + 2 parameters to get the sizes.)
563 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
564 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
565 *
566 * @param pClient The client state.
567 * @param hCall The client's call handle.
568 * @param cParms Number of parameters.
569 * @param paParms Array of parameters.
570 */
571int vboxSvcClipboardMsgGet(PVBOXCLIPBOARDCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
572{
573 /*
574 * Validate the request.
575 */
576 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
577 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
578 : UINT32_MAX;
579
580 /*
581 * Return information about the first message if one is pending in the list.
582 */
583 if (!pClient->queueMsg.isEmpty())
584 {
585 PVBOXCLIPBOARDCLIENTMSG pFirstMsg = pClient->queueMsg.first();
586 if (pFirstMsg)
587 {
588 LogFlowFunc(("First message is: %RU32 (%s), cParms=%RU32\n",
589 pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg), pFirstMsg->m_cParms));
590
591 ASSERT_GUEST_MSG_RETURN(pFirstMsg->m_uMsg == idMsgExpected || idMsgExpected == UINT32_MAX,
592 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
593 pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg), pFirstMsg->m_cParms,
594 idMsgExpected, VBoxClipboardHostMsgToStr(idMsgExpected), cParms),
595 VERR_MISMATCH);
596 ASSERT_GUEST_MSG_RETURN(pFirstMsg->m_cParms == cParms,
597 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
598 pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg), pFirstMsg->m_cParms,
599 idMsgExpected, VBoxClipboardHostMsgToStr(idMsgExpected), cParms),
600 VERR_WRONG_PARAMETER_COUNT);
601
602 /* Check the parameter types. */
603 for (uint32_t i = 0; i < cParms; i++)
604 ASSERT_GUEST_MSG_RETURN(pFirstMsg->m_paParms[i].type == paParms[i].type,
605 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->m_paParms[i].type,
606 paParms[i].type, pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg)),
607 VERR_WRONG_PARAMETER_TYPE);
608 /*
609 * Copy out the parameters.
610 *
611 * No assertions on buffer overflows, and keep going till the end so we can
612 * communicate all the required buffer sizes.
613 */
614 int rc = VINF_SUCCESS;
615 for (uint32_t i = 0; i < cParms; i++)
616 switch (pFirstMsg->m_paParms[i].type)
617 {
618 case VBOX_HGCM_SVC_PARM_32BIT:
619 paParms[i].u.uint32 = pFirstMsg->m_paParms[i].u.uint32;
620 break;
621
622 case VBOX_HGCM_SVC_PARM_64BIT:
623 paParms[i].u.uint64 = pFirstMsg->m_paParms[i].u.uint64;
624 break;
625
626 case VBOX_HGCM_SVC_PARM_PTR:
627 {
628 uint32_t const cbSrc = pFirstMsg->m_paParms[i].u.pointer.size;
629 uint32_t const cbDst = paParms[i].u.pointer.size;
630 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
631 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
632 if (cbSrc <= cbDst)
633 memcpy(paParms[i].u.pointer.addr, pFirstMsg->m_paParms[i].u.pointer.addr, cbSrc);
634 else
635 {
636 AssertMsgFailed(("#%u: cbSrc=%RU32 is bigger than cbDst=%RU32\n", i, cbSrc, cbDst));
637 rc = VERR_BUFFER_OVERFLOW;
638 }
639 break;
640 }
641
642 default:
643 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->m_paParms[i].type));
644 rc = VERR_INTERNAL_ERROR;
645 break;
646 }
647 if (RT_SUCCESS(rc))
648 {
649 /*
650 * Complete the message and remove the pending message unless the
651 * guest raced us and cancelled this call in the meantime.
652 */
653 AssertPtr(g_pHelpers);
654 rc = g_pHelpers->pfnCallComplete(hCall, rc);
655
656 LogFlowFunc(("[Client %RU32] pfnCallComplete -> %Rrc\n", pClient->uClientID, rc));
657
658 if (rc != VERR_CANCELLED)
659 {
660 pClient->queueMsg.removeFirst();
661 vboxSvcClipboardMsgFree(pFirstMsg);
662 }
663
664 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
665 }
666
667 LogFlowFunc(("[Client %RU32] Returning %Rrc\n", pClient->uClientID, rc));
668 return rc;
669 }
670 }
671
672 paParms[0].u.uint32 = 0;
673 paParms[1].u.uint32 = 0;
674 LogFlowFunc(("[Client %RU32] -> VERR_TRY_AGAIN\n", pClient->uClientID));
675 return VERR_TRY_AGAIN;
676}
677
678int vboxSvcClipboardClientWakeup(PVBOXCLIPBOARDCLIENT pClient)
679{
680 int rc = VINF_NO_CHANGE;
681
682 if (pClient->Pending.uType)
683 {
684 LogFlowFunc(("[Client %RU32] Waking up ...\n", pClient->uClientID));
685
686 rc = VINF_SUCCESS;
687
688 if (!pClient->queueMsg.isEmpty())
689 {
690 PVBOXCLIPBOARDCLIENTMSG pFirstMsg = pClient->queueMsg.first();
691 if (pFirstMsg)
692 {
693 LogFlowFunc(("[Client %RU32] Current host message is %RU32 (%s), cParms=%RU32\n",
694 pClient->uClientID, pFirstMsg->m_uMsg, VBoxClipboardHostMsgToStr(pFirstMsg->m_uMsg),
695 pFirstMsg->m_cParms));
696
697 if (pClient->Pending.uType == VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_WAIT)
698 {
699 vboxSvcClipboardMsgSetPeekReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
700 rc = g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
701
702 pClient->Pending.hHandle = NULL;
703 pClient->Pending.paParms = NULL;
704 pClient->Pending.cParms = 0;
705 pClient->Pending.uType = false;
706 }
707 }
708 else
709 AssertFailed();
710 }
711 else
712 AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", pClient->uClientID));
713
714 return rc;
715 }
716
717 return VINF_NO_CHANGE;
718}
719
720/**
721 * Set the HGCM parameters according to pending messages.
722 * Executed under the clipboard lock.
723 *
724 * Legacy protocol, do not use anymore.
725 */
726static bool vboxSvcClipboardOldReturnMsg(PVBOXCLIPBOARDCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
727{
728 /** @todo r=andy The client at the moment supplies two parameters, which we can
729 * use by filling in the next message type sent by the host service.
730 * Make this more flexible later, as I don't want to break the existing protocol right now. */
731 if (cParms < 2)
732 {
733 AssertFailed(); /* Should never happen. */
734 return false;
735 }
736
737 /* Message priority is taken into account. */
738 if (pClient->State.Old.fHostMsgQuit)
739 {
740 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT\n"));
741 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT);
742 HGCMSvcSetU32(&paParms[1], 0);
743 pClient->State.Old.fHostMsgQuit = false;
744 }
745 else if (pClient->State.Old.fHostMsgReadData)
746 {
747 uint32_t fFormat = 0;
748
749 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA: u32RequestedFormat=%02X\n",
750 pClient->State.Old.u32RequestedFormat));
751
752 if (pClient->State.Old.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT)
753 fFormat = VBOX_SHARED_CLIPBOARD_FMT_UNICODETEXT;
754 else if (pClient->State.Old.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_BITMAP)
755 fFormat = VBOX_SHARED_CLIPBOARD_FMT_BITMAP;
756 else if (pClient->State.Old.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_HTML)
757 fFormat = VBOX_SHARED_CLIPBOARD_FMT_HTML;
758#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
759 else if (pClient->State.Old.u32RequestedFormat & VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
760 fFormat = VBOX_SHARED_CLIPBOARD_FMT_URI_LIST;
761#endif
762 else
763 {
764 LogRel2(("Clipboard: Unsupported format from guest (0x%x), skipping\n", fFormat));
765 pClient->State.Old.u32RequestedFormat = 0;
766 }
767 pClient->State.Old.u32RequestedFormat &= ~fFormat;
768 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA);
769 HGCMSvcSetU32(&paParms[1], fFormat);
770 if (pClient->State.Old.u32RequestedFormat == 0)
771 pClient->State.Old.fHostMsgReadData = false;
772 }
773 else if (pClient->State.Old.fHostMsgFormats)
774 {
775 LogFlowFunc(("VBOX_SHARED_CLIPBOARD_HOST_MSG_REPORT_FORMATS: u32AvailableFormats=%02X\n",
776 pClient->State.Old.u32AvailableFormats));
777
778 HGCMSvcSetU32(&paParms[0], VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS_WRITE);
779 HGCMSvcSetU32(&paParms[1], pClient->State.Old.u32AvailableFormats);
780 pClient->State.Old.fHostMsgFormats = false;
781 }
782 else
783 {
784 /* No pending messages. */
785 LogFlowFunc(("No pending message\n"));
786 return false;
787 }
788
789 /* Message information assigned. */
790 return true;
791}
792
793int vboxSvcClipboardSendFormatsWrite(PVBOXCLIPBOARDCLIENT pClient, PSHAREDCLIPBOARDFORMATDATA pFormats)
794{
795 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
796
797 int rc;
798
799 PVBOXCLIPBOARDCLIENTMSG pMsg = vboxSvcClipboardMsgAlloc(VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS_WRITE, 3);
800 if (pMsg)
801 {
802 VBOXCLIPBOARDEVENTID uEvent = SharedClipboardEventIDGenerate(&pClient->Events);
803
804 HGCMSvcSetU32(&pMsg->m_paParms[0], VBOX_SHARED_CLIPBOARD_CONTEXTID_MAKE(pClient->Events.uID, uEvent));
805 HGCMSvcSetU32(&pMsg->m_paParms[1], pFormats->uFormats);
806 HGCMSvcSetU32(&pMsg->m_paParms[2], 0 /* fFlags */);
807
808 rc = vboxSvcClipboardMsgAdd(pClient, pMsg, true /* fAppend */);
809 if (RT_SUCCESS(rc))
810 {
811 rc = vboxSvcClipboardClientWakeup(pClient);
812 }
813 }
814 else
815 rc = VERR_NO_MEMORY;
816
817 LogFlowFuncLeaveRC(rc);
818 return rc;
819}
820
821int vboxSvcClipboardGetDataWrite(PVBOXCLIPBOARDCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
822{
823 LogFlowFuncEnter();
824
825 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
826 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
827 {
828 return VERR_NOT_SUPPORTED;
829 }
830
831 int rc;
832
833 SHAREDCLIPBOARDDATABLOCK dataBlock;
834 RT_ZERO(dataBlock);
835
836 VBOXCLIPBOARDCLIENTCMDCTX cmdCtx;
837 RT_ZERO(cmdCtx);
838
839 if (pClient->State.uProtocolVer == 0) /* Legacy protocol */
840 {
841 if (cParms < 2)
842 {
843 rc = VERR_INVALID_PARAMETER;
844 }
845 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
846 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR) /* ptr */
847 {
848 rc = VERR_INVALID_PARAMETER;
849 }
850 else
851 {
852 rc = HGCMSvcGetU32(&paParms[0], &dataBlock.uFormat);
853 if (RT_SUCCESS(rc))
854 rc = HGCMSvcGetBuf(&paParms[1], &dataBlock.pvData, &dataBlock.cbData);
855 }
856 }
857 else
858 {
859 if (cParms < VBOX_SHARED_CLIPBOARD_CPARMS_WRITE_DATA)
860 {
861 rc = VERR_INVALID_PARAMETER;
862 }
863 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* uContext */
864 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uFormat */
865 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbData */
866 || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR) /* pvData */
867 {
868 rc = VERR_INVALID_PARAMETER;
869 }
870 else
871 {
872 rc = HGCMSvcGetU32(&paParms[0], &cmdCtx.uContextID);
873 if (RT_SUCCESS(rc))
874 rc = HGCMSvcGetU32(&paParms[1], &dataBlock.uFormat);
875 if (RT_SUCCESS(rc))
876 rc = HGCMSvcGetBuf(&paParms[3], &dataBlock.pvData, &dataBlock.cbData);
877
878 /** @todo Handle the rest. */
879 }
880 }
881
882 if (RT_SUCCESS(rc))
883 {
884 if (g_ExtState.pfnExtension)
885 {
886 VBOXCLIPBOARDEXTPARMS parms;
887 RT_ZERO(parms);
888
889 parms.uFormat = dataBlock.uFormat;
890 parms.u.pvData = dataBlock.pvData;
891 parms.cbData = dataBlock.cbData;
892
893 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof(parms));
894 }
895
896 rc = VBoxClipboardSvcImplWriteData(pClient, &cmdCtx, &dataBlock);
897 }
898
899 LogFlowFuncLeaveRC(rc);
900 return rc;
901}
902
903/**
904 * Reports a cached message back to the guest.
905 *
906 * Legacy protocol, do not use anymore.
907 */
908int vboxSvcClipboardOldReportMsg(PVBOXCLIPBOARDCLIENT pClient, uint32_t uMsg, uint32_t uFormats)
909{
910 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
911
912 int rc = VINF_SUCCESS;
913
914 LogFlowFunc(("uMsg=%RU32 (%s), fIsAsync=%RTbool\n",
915 uMsg, VBoxClipboardHostMsgToStr(uMsg), pClient->State.Old.fAsync));
916
917 if (VBoxSvcClipboardLock())
918 {
919 switch (uMsg)
920 {
921 case VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT:
922 {
923 pClient->State.Old.fHostMsgQuit = true;
924 } break;
925
926 case VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA:
927 {
928 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
929 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
930 {
931 /* Skip the message. */
932 break;
933 }
934
935 LogFlowFunc(("uFormats=%02X\n", uFormats));
936
937 pClient->State.Old.u32RequestedFormat = uFormats;
938 pClient->State.Old.fHostMsgReadData = true;
939 } break;
940
941 case VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS_WRITE:
942 {
943 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
944 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
945 {
946 /* Skip the message. */
947 break;
948 }
949
950 LogFlowFunc(("uFormats=%02X\n", uFormats));
951
952 pClient->State.Old.u32AvailableFormats = uFormats;
953 pClient->State.Old.fHostMsgFormats = true;
954 } break;
955
956 default:
957 {
958 AssertMsgFailed(("Invalid message %RU32\n", uMsg));
959 rc = VERR_INVALID_PARAMETER;
960 break;
961 }
962 }
963
964 if (RT_SUCCESS(rc))
965 {
966 if (pClient->State.Old.fAsync)
967 {
968 /* The client waits for a response. */
969 bool fMessageReturned = vboxSvcClipboardOldReturnMsg(pClient,
970 pClient->State.Old.async.cParms,
971 pClient->State.Old.async.paParms);
972
973 /* Make a copy of the handle. */
974 VBOXHGCMCALLHANDLE callHandle = pClient->State.Old.async.callHandle;
975
976 if (fMessageReturned)
977 {
978 /* There is a response. */
979 pClient->State.Old.fAsync = false;
980 }
981
982 VBoxSvcClipboardUnlock();
983
984 if (fMessageReturned)
985 {
986 LogFlowFunc(("CallComplete\n"));
987 g_pHelpers->pfnCallComplete(callHandle, VINF_SUCCESS);
988 }
989 }
990 else
991 VBoxSvcClipboardUnlock();
992 }
993 else
994 VBoxSvcClipboardUnlock();
995 }
996
997 LogFlowFuncLeaveRC(rc);
998 return rc;
999}
1000
1001
1002int vboxSvcClipboardSetSource(PVBOXCLIPBOARDCLIENT pClient, SHAREDCLIPBOARDSOURCE enmSource)
1003{
1004 if (!pClient) /* If no client connected (anymore), bail out. */
1005 return VINF_SUCCESS;
1006
1007 int rc = VINF_SUCCESS;
1008
1009 if (VBoxSvcClipboardLock())
1010 {
1011 pClient->State.enmSource = enmSource;
1012
1013 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.u32ClientID, pClient->State.enmSource));
1014
1015 VBoxSvcClipboardUnlock();
1016 }
1017
1018 LogFlowFuncLeaveRC(rc);
1019 return rc;
1020}
1021
1022#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1023int vboxSvcClipboardURITransferStart(PVBOXCLIPBOARDCLIENT pClient,
1024 SHAREDCLIPBOARDURITRANSFERDIR enmDir, SHAREDCLIPBOARDSOURCE enmSource,
1025 PSHAREDCLIPBOARDURITRANSFER *ppTransfer)
1026{
1027 LogFlowFuncEnter();
1028
1029 SharedClipboardURICtxTransfersCleanup(&pClient->URI);
1030
1031 int rc;
1032
1033 if (!SharedClipboardURICtxTransfersMaximumReached(&pClient->URI))
1034 {
1035 PSHAREDCLIPBOARDURITRANSFER pTransfer;
1036 rc = SharedClipboardURITransferCreate(enmDir, enmSource, &pTransfer);
1037 if (RT_SUCCESS(rc))
1038 {
1039 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
1040 RT_ZERO(creationCtx);
1041
1042 if (enmDir == SHAREDCLIPBOARDURITRANSFERDIR_READ)
1043 {
1044 rc = vboxSvcClipboardURIAreaRegister(&pClient->State, pTransfer);
1045 if (RT_SUCCESS(rc))
1046 {
1047 creationCtx.enmSource = pClient->State.enmSource;
1048
1049 creationCtx.Interface.pfnTransferOpen = vboxSvcClipboardURITransferOpen;
1050 creationCtx.Interface.pfnTransferClose = vboxSvcClipboardURITransferClose;
1051 creationCtx.Interface.pfnListOpen = vboxSvcClipboardURIListOpen;
1052 creationCtx.Interface.pfnListClose = vboxSvcClipboardURIListClose;
1053 creationCtx.Interface.pfnObjOpen = vboxSvcClipboardURIObjOpen;
1054 creationCtx.Interface.pfnObjClose = vboxSvcClipboardURIObjClose;
1055
1056 creationCtx.Interface.pfnGetRoots = vboxSvcClipboardURIGetRoots;
1057 creationCtx.Interface.pfnListHdrRead = vboxSvcClipboardURIListHdrRead;
1058 creationCtx.Interface.pfnListEntryRead = vboxSvcClipboardURIListEntryRead;
1059 creationCtx.Interface.pfnObjRead = vboxSvcClipboardURIObjRead;
1060
1061 creationCtx.pvUser = pClient;
1062 }
1063 }
1064 else if (enmDir == SHAREDCLIPBOARDURITRANSFERDIR_WRITE)
1065 {
1066 AssertFailed(); /** @todo Implement this. */
1067 }
1068
1069 /* Register needed callbacks so that we can wait for the meta data to arrive here. */
1070 SHAREDCLIPBOARDURITRANSFERCALLBACKS Callbacks;
1071 RT_ZERO(Callbacks);
1072
1073 Callbacks.pvUser = pClient;
1074
1075 Callbacks.pfnTransferPrepare = VBoxSvcClipboardURITransferPrepareCallback;
1076 Callbacks.pfnTransferComplete = VBoxSvcClipboardURITransferCompleteCallback;
1077 Callbacks.pfnTransferCanceled = VBoxSvcClipboardURITransferCanceledCallback;
1078 Callbacks.pfnTransferError = VBoxSvcClipboardURITransferErrorCallback;
1079
1080 SharedClipboardURITransferSetCallbacks(pTransfer, &Callbacks);
1081
1082 rc = SharedClipboardURITransferSetInterface(pTransfer, &creationCtx);
1083 if (RT_SUCCESS(rc))
1084 {
1085 rc = SharedClipboardURICtxTransferAdd(&pClient->URI, pTransfer);
1086 if (RT_SUCCESS(rc))
1087 rc = VBoxClipboardSvcImplURITransferCreate(pClient, pTransfer);
1088
1089 if (RT_FAILURE(rc))
1090 VBoxClipboardSvcImplURITransferDestroy(pClient, pTransfer);
1091 }
1092
1093 if (RT_FAILURE(rc))
1094 {
1095 SharedClipboardURITransferDestroy(pTransfer);
1096 pTransfer = NULL;
1097 }
1098 else
1099 {
1100 *ppTransfer = pTransfer;
1101 }
1102 }
1103 }
1104 else
1105 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
1106
1107 LogFlowFuncLeaveRC(rc);
1108 return rc;
1109}
1110#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
1111
1112static int svcInit(void)
1113{
1114 int rc = RTCritSectInit(&g_CritSect);
1115
1116 if (RT_SUCCESS(rc))
1117 {
1118 vboxSvcClipboardModeSet(VBOX_SHARED_CLIPBOARD_MODE_OFF);
1119
1120 rc = VBoxClipboardSvcImplInit();
1121
1122 /* Clean up on failure, because 'svnUnload' will not be called
1123 * if the 'svcInit' returns an error.
1124 */
1125 if (RT_FAILURE(rc))
1126 {
1127 RTCritSectDelete(&g_CritSect);
1128 }
1129 }
1130
1131 return rc;
1132}
1133
1134static DECLCALLBACK(int) svcUnload(void *)
1135{
1136 LogFlowFuncEnter();
1137
1138 VBoxClipboardSvcImplDestroy();
1139
1140 RTCritSectDelete(&g_CritSect);
1141
1142 return VINF_SUCCESS;
1143}
1144
1145/**
1146 * Disconnect the host side of the shared clipboard and send a "host disconnected" message
1147 * to the guest side.
1148 */
1149static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
1150{
1151 RT_NOREF(u32ClientID, pvClient);
1152
1153 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1154
1155 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)pvClient;
1156 AssertPtr(pClient);
1157
1158 vboxSvcClipboardOldReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_QUIT, 0); /** @todo r=andy Why is this necessary? The client already disconnected ... */
1159 vboxSvcClipboardOldCompleteReadData(pClient, VERR_NO_DATA, 0);
1160
1161#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1162 PSHAREDCLIPBOARDURITRANSFER pTransfer = SharedClipboardURICtxGetTransfer(&pClient->URI, 0 /* Index*/);
1163 if (pTransfer)
1164 vboxSvcClipboardURIAreaDetach(&pClient->State, pTransfer);
1165
1166 SharedClipboardURICtxDestroy(&pClient->URI);
1167#endif
1168
1169 VBoxClipboardSvcImplDisconnect(pClient);
1170
1171 vboxSvcClipboardClientStateReset(&pClient->State);
1172 vboxSvcClipboardClientStateDestroy(&pClient->State);
1173
1174 ClipboardClientMap::iterator itClient = g_mapClients.find(u32ClientID);
1175 if (itClient != g_mapClients.end())
1176 {
1177 g_mapClients.erase(itClient);
1178 }
1179 else
1180 AssertFailed();
1181
1182 return VINF_SUCCESS;
1183}
1184
1185static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
1186{
1187 RT_NOREF(fRequestor, fRestoring);
1188
1189 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)pvClient;
1190 AssertPtr(pvClient);
1191
1192 /* Assign the client ID. */
1193 pClient->uClientID = u32ClientID;
1194
1195 /* Create the client's own event source. */
1196 VBOXCLIPBOARDEVENTSOURCEID uEventSourceID;
1197 int rc = vboxSvcClipboardEventSourceCreateID(&uEventSourceID);
1198 if (RT_SUCCESS(rc))
1199 rc = SharedClipboardEventSourceCreate(&pClient->Events, uEventSourceID);
1200 if (RT_SUCCESS(rc))
1201 {
1202 LogFlowFunc(("[Client %RU32] Using event source %RU32\n", u32ClientID, pClient->Events.uID));
1203
1204 /* Reset the client state. */
1205 vboxSvcClipboardClientStateReset(&pClient->State);
1206
1207 /* (Re-)initialize the client state. */
1208 rc = vboxSvcClipboardClientStateInit(&pClient->State, u32ClientID);
1209 if (RT_SUCCESS(rc))
1210 {
1211 rc = VBoxClipboardSvcImplConnect(pClient, VBoxSvcClipboardGetHeadless());
1212#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1213 if (RT_SUCCESS(rc))
1214 rc = SharedClipboardURICtxInit(&pClient->URI);
1215#endif
1216 if (RT_SUCCESS(rc))
1217 {
1218 /* Assign weak pointer to client map .*/
1219 g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
1220
1221 /* For now we ASSUME that the first client ever connected is in charge for
1222 * communicating withe the service extension.
1223 *
1224 ** @todo This needs to be fixed ASAP w/o breaking older guest / host combos. */
1225 if (g_ExtState.uClientID == 0)
1226 g_ExtState.uClientID = u32ClientID;
1227 }
1228 }
1229 }
1230
1231 LogFlowFuncLeaveRC(rc);
1232 return rc;
1233}
1234
1235static DECLCALLBACK(void) svcCall(void *,
1236 VBOXHGCMCALLHANDLE callHandle,
1237 uint32_t u32ClientID,
1238 void *pvClient,
1239 uint32_t u32Function,
1240 uint32_t cParms,
1241 VBOXHGCMSVCPARM paParms[],
1242 uint64_t tsArrival)
1243{
1244 RT_NOREF(u32ClientID, pvClient, tsArrival);
1245
1246 int rc = VINF_SUCCESS;
1247
1248 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)pvClient;
1249 AssertPtr(pClient);
1250
1251 LogFunc(("u32ClientID=%RU32 (proto %RU32), fn=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1252 u32ClientID, pClient->State.uProtocolVer, u32Function, VBoxClipboardGuestMsgToStr(u32Function), cParms, paParms));
1253
1254#ifdef DEBUG
1255 uint32_t i;
1256
1257 for (i = 0; i < cParms; i++)
1258 {
1259 /** @todo parameters other than 32 bit */
1260 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
1261 }
1262#endif
1263
1264 bool fDefer = false;
1265
1266 switch (u32Function)
1267 {
1268 case VBOX_SHARED_CLIPBOARD_GUEST_FN_GET_HOST_MSG_OLD:
1269 {
1270 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_GET_HOST_MSG_OLD)
1271 {
1272 rc = VERR_INVALID_PARAMETER;
1273 }
1274 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
1275 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
1276 {
1277 rc = VERR_INVALID_PARAMETER;
1278 }
1279 else
1280 {
1281 /* Atomically verify the client's state. */
1282 if (VBoxSvcClipboardLock())
1283 {
1284 bool fMessageReturned = vboxSvcClipboardOldReturnMsg(pClient, cParms, paParms);
1285 if (fMessageReturned)
1286 {
1287 /* Just return to the caller. */
1288 pClient->State.Old.fAsync = false;
1289 }
1290 else
1291 {
1292 /* No event available at the time. Process asynchronously. */
1293 fDefer = true;
1294
1295 pClient->State.Old.fAsync = true;
1296 pClient->State.Old.async.callHandle = callHandle;
1297 pClient->State.Old.async.cParms = cParms;
1298 pClient->State.Old.async.paParms = paParms;
1299 }
1300
1301 VBoxSvcClipboardUnlock();
1302 }
1303 else
1304 {
1305 rc = VERR_NOT_SUPPORTED;
1306 }
1307 }
1308
1309 break;
1310 }
1311
1312 case VBOX_SHARED_CLIPBOARD_GUEST_FN_CONNECT:
1313 {
1314 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_CONNECT)
1315 {
1316 rc = VERR_INVALID_PARAMETER;
1317 }
1318 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* uProtocolVer */
1319 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uProtocolFlags */
1320 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* cbChunkSize */
1321 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* enmCompression */
1322 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT) /* enmChecksumType */
1323 {
1324 rc = VERR_INVALID_PARAMETER;
1325 }
1326 else if (vboxSvcClipboardGetMode() == VBOX_SHARED_CLIPBOARD_MODE_OFF)
1327 {
1328 rc = VERR_ACCESS_DENIED;
1329 }
1330 else
1331 {
1332 /* Update the protocol version and tell the guest. */
1333 pClient->State.uProtocolVer = 1;
1334
1335 LogFlowFunc(("Now using protocol v%RU32\n", pClient->State.uProtocolVer));
1336
1337 HGCMSvcSetU32(&paParms[0], pClient->State.uProtocolVer);
1338 HGCMSvcSetU32(&paParms[1], 0 /* Procotol flags, not used yet */);
1339 HGCMSvcSetU32(&paParms[2], pClient->State.cbChunkSize);
1340 HGCMSvcSetU32(&paParms[3], 0 /* Compression type, not used yet */);
1341 HGCMSvcSetU32(&paParms[4], 0 /* Checksum type, not used yet */);
1342
1343 rc = VINF_SUCCESS;
1344 }
1345
1346 break;
1347 }
1348
1349 case VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_NOWAIT:
1350 {
1351 rc = vboxSvcClipboardMsgPeek(pClient, callHandle, cParms, paParms, false /*fWait*/);
1352 break;
1353 }
1354
1355 case VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_PEEK_WAIT:
1356 {
1357 rc = vboxSvcClipboardMsgPeek(pClient, callHandle, cParms, paParms, true /*fWait*/);
1358 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1359 fDefer = true;
1360 break;
1361 }
1362
1363 case VBOX_SHARED_CLIPBOARD_GUEST_FN_MSG_GET:
1364 {
1365 rc = vboxSvcClipboardMsgGet(pClient, callHandle, cParms, paParms);
1366 if (RT_SUCCESS(rc)) /* vboxSvcClipboardMsgGet did the completion already. */
1367 fDefer = true;
1368 break;
1369 }
1370
1371 case VBOX_SHARED_CLIPBOARD_GUEST_FN_FORMATS_WRITE:
1372 {
1373 uint32_t uFormats = 0;
1374
1375 if (pClient->State.uProtocolVer == 0)
1376 {
1377 if (cParms != 1)
1378 {
1379 rc = VERR_INVALID_PARAMETER;
1380 }
1381 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* uFormats */
1382 {
1383 rc = VERR_INVALID_PARAMETER;
1384 }
1385 else
1386 {
1387 rc = HGCMSvcGetU32(&paParms[0], &uFormats);
1388 }
1389 }
1390 else
1391 {
1392 if (cParms != 3)
1393 {
1394 rc = VERR_INVALID_PARAMETER;
1395 }
1396 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* uContextID */
1397 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uFormats */
1398 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* fFlags */
1399 {
1400 rc = VERR_INVALID_PARAMETER;
1401 }
1402 else
1403 {
1404 rc = HGCMSvcGetU32(&paParms[1], &uFormats);
1405
1406 /** @todo Handle rest. */
1407 }
1408 }
1409
1410 if (RT_SUCCESS(rc))
1411 {
1412 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST
1413 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
1414 {
1415 rc = VERR_ACCESS_DENIED;
1416 }
1417 else
1418 {
1419 rc = vboxSvcClipboardSetSource(pClient, SHAREDCLIPBOARDSOURCE_REMOTE);
1420 if (RT_SUCCESS(rc))
1421 {
1422 if (g_ExtState.pfnExtension)
1423 {
1424 VBOXCLIPBOARDEXTPARMS parms;
1425 RT_ZERO(parms);
1426
1427 parms.uFormat = uFormats;
1428
1429 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof(parms));
1430 }
1431
1432 VBOXCLIPBOARDCLIENTCMDCTX cmdCtx;
1433 RT_ZERO(cmdCtx);
1434
1435 SHAREDCLIPBOARDFORMATDATA formatData;
1436 RT_ZERO(formatData);
1437
1438 formatData.uFormats = uFormats;
1439
1440 rc = VBoxClipboardSvcImplFormatAnnounce(pClient, &cmdCtx, &formatData);
1441 }
1442 }
1443 }
1444
1445 break;
1446 }
1447
1448 case VBOX_SHARED_CLIPBOARD_GUEST_FN_DATA_READ:
1449 {
1450 if (cParms != VBOX_SHARED_CLIPBOARD_CPARMS_READ_DATA)
1451 {
1452 rc = VERR_INVALID_PARAMETER;
1453 }
1454 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* format */
1455 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* ptr */
1456 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* size */
1457 )
1458 {
1459 rc = VERR_INVALID_PARAMETER;
1460 }
1461 else
1462 {
1463 if ( vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST
1464 && vboxSvcClipboardGetMode() != VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL)
1465 {
1466 rc = VERR_ACCESS_DENIED;
1467 break;
1468 }
1469
1470 uint32_t u32Format;
1471 rc = HGCMSvcGetU32(&paParms[0], &u32Format);
1472 if (RT_SUCCESS(rc))
1473 {
1474#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1475 if (u32Format == VBOX_SHARED_CLIPBOARD_FMT_URI_LIST)
1476 {
1477 if (!SharedClipboardURICtxTransfersMaximumReached(&pClient->URI))
1478 {
1479 SharedClipboardURICtxTransfersCleanup(&pClient->URI);
1480
1481 PSHAREDCLIPBOARDURITRANSFER pTransfer;
1482 rc = SharedClipboardURITransferCreate(SHAREDCLIPBOARDURITRANSFERDIR_WRITE,
1483 pClient->State.enmSource,
1484 &pTransfer);
1485 if (RT_SUCCESS(rc))
1486 {
1487 /* Attach to the most recent clipboard area. */
1488 rc = vboxSvcClipboardURIAreaAttach(&pClient->State, pTransfer, 0 /* Area ID */);
1489 if (RT_SUCCESS(rc))
1490 {
1491 SHAREDCLIPBOARDPROVIDERCREATIONCTX creationCtx;
1492 RT_ZERO(creationCtx);
1493
1494 creationCtx.enmSource = SharedClipboardURITransferGetSource(pTransfer);
1495
1496 RT_ZERO(creationCtx.Interface);
1497
1498 creationCtx.Interface.pfnListHdrWrite = vboxSvcClipboardURIListHdrWrite;
1499 creationCtx.Interface.pfnListEntryWrite = vboxSvcClipboardURIListEntryWrite;
1500 creationCtx.Interface.pfnObjWrite = vboxSvcClipboardURIObjWrite;
1501
1502 creationCtx.pvUser = pClient;
1503
1504 rc = SharedClipboardURITransferSetInterface(pTransfer, &creationCtx);
1505 if (RT_SUCCESS(rc))
1506 rc = SharedClipboardURICtxTransferAdd(&pClient->URI, pTransfer);
1507 }
1508
1509 if (RT_SUCCESS(rc))
1510 {
1511 rc = VBoxClipboardSvcImplURITransferCreate(pClient, pTransfer);
1512 }
1513 else
1514 {
1515 VBoxClipboardSvcImplURITransferDestroy(pClient, pTransfer);
1516 SharedClipboardURITransferDestroy(pTransfer);
1517 }
1518 }
1519 }
1520 else
1521 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
1522
1523 if (RT_FAILURE(rc))
1524 LogRel(("Shared Clipboard: Initializing URI host to guest write transfer failed with %Rrc\n", rc));
1525 }
1526 else
1527 {
1528#endif /* VBOX_WITH_SHARED_CLIPBOARD_URI_LIST */
1529 void *pv;
1530 uint32_t cb;
1531 rc = HGCMSvcGetBuf(&paParms[1], &pv, &cb);
1532 if (RT_SUCCESS(rc))
1533 {
1534 uint32_t cbActual = 0;
1535
1536 /* If there is a service extension active, try reading data from it first. */
1537 if (g_ExtState.pfnExtension)
1538 {
1539 VBOXCLIPBOARDEXTPARMS parms;
1540 RT_ZERO(parms);
1541
1542 parms.uFormat = u32Format;
1543 parms.u.pvData = pv;
1544 parms.cbData = cb;
1545
1546 g_ExtState.fReadingData = true;
1547
1548 /* Read clipboard data from the extension. */
1549 rc = g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ,
1550 &parms, sizeof(parms));
1551
1552 LogFlowFunc(("g_ExtState.fDelayedAnnouncement=%RTbool, g_ExtState.uDelayedFormats=0x%x\n",
1553 g_ExtState.fDelayedAnnouncement, g_ExtState.uDelayedFormats));
1554
1555 /* Did the extension send the clipboard formats yet?
1556 * Otherwise, do this now. */
1557 if (g_ExtState.fDelayedAnnouncement)
1558 {
1559 vboxSvcClipboardOldReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS_WRITE,
1560 g_ExtState.uDelayedFormats);
1561
1562 g_ExtState.fDelayedAnnouncement = false;
1563 g_ExtState.uDelayedFormats = 0;
1564 }
1565
1566 g_ExtState.fReadingData = false;
1567
1568 if (RT_SUCCESS(rc))
1569 {
1570 cbActual = parms.cbData;
1571 }
1572 }
1573
1574 /* Note: The host clipboard *always* has precedence over the service extension above,
1575 * so data which has been read above might get overridden by the host clipboard eventually. */
1576
1577 /* Release any other pending read, as we only
1578 * support one pending read at one time. */
1579 rc = vboxSvcClipboardOldCompleteReadData(pClient, VERR_NO_DATA, 0);
1580 if (RT_SUCCESS(rc))
1581 {
1582 VBOXCLIPBOARDCLIENTCMDCTX cmdCtx;
1583 RT_ZERO(cmdCtx);
1584
1585 SHAREDCLIPBOARDDATABLOCK dataBlock;
1586 RT_ZERO(dataBlock);
1587
1588 dataBlock.pvData = pv;
1589 dataBlock.cbData = cb;
1590 dataBlock.uFormat = u32Format;
1591
1592 rc = VBoxClipboardSvcImplReadData(pClient, &cmdCtx, &dataBlock, &cbActual);
1593 }
1594
1595 /*
1596 * Remember our read request until it is completed.
1597 * See the protocol description above for more information.
1598 */
1599 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1600 {
1601 if (VBoxSvcClipboardLock())
1602 {
1603 pClient->State.Old.asyncRead.callHandle = callHandle;
1604 pClient->State.Old.asyncRead.cParms = cParms;
1605 pClient->State.Old.asyncRead.paParms = paParms;
1606 pClient->State.Old.fReadPending = true;
1607 fDefer = true;
1608 VBoxSvcClipboardUnlock();
1609 }
1610 else
1611 rc = VERR_NOT_SUPPORTED;
1612 }
1613 else if (RT_SUCCESS (rc))
1614 {
1615 HGCMSvcSetU32(&paParms[2], cbActual);
1616 }
1617 }
1618#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1619 }
1620#endif
1621 }
1622 }
1623
1624 break;
1625 }
1626
1627 case VBOX_SHARED_CLIPBOARD_GUEST_FN_DATA_WRITE:
1628 {
1629 rc = vboxSvcClipboardGetDataWrite(pClient, cParms, paParms);
1630 break;
1631 }
1632
1633 default:
1634 {
1635#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1636 rc = vboxSvcClipboardURIHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
1637
1638 /* The URI handler does deferring on its own, so never do any call completion here. */
1639 fDefer = true;
1640#else
1641 rc = VERR_NOT_IMPLEMENTED;
1642#endif
1643 break;
1644 }
1645 }
1646
1647 LogFlowFunc(("u32ClientID=%RU32, fDefer=%RTbool\n", pClient->uClientID, fDefer));
1648
1649 if (!fDefer)
1650 g_pHelpers->pfnCallComplete(callHandle, rc);
1651
1652 LogFlowFuncLeaveRC(rc);
1653}
1654
1655/** If the client in the guest is waiting for a read operation to complete
1656 * then complete it, otherwise return. See the protocol description in the
1657 * shared clipboard module description. */
1658int vboxSvcClipboardOldCompleteReadData(PVBOXCLIPBOARDCLIENT pClient, int rc, uint32_t cbActual)
1659{
1660 VBOXHGCMCALLHANDLE callHandle = NULL;
1661 VBOXHGCMSVCPARM *paParms = NULL;
1662 bool fReadPending = false;
1663 if (VBoxSvcClipboardLock()) /* if not can we do anything useful? */
1664 {
1665 callHandle = pClient->State.Old.asyncRead.callHandle;
1666 paParms = pClient->State.Old.asyncRead.paParms;
1667 fReadPending = pClient->State.Old.fReadPending;
1668 pClient->State.Old.fReadPending = false;
1669 VBoxSvcClipboardUnlock();
1670 }
1671 if (fReadPending)
1672 {
1673 HGCMSvcSetU32(&paParms[2], cbActual);
1674 g_pHelpers->pfnCallComplete(callHandle, rc);
1675 }
1676
1677 return VINF_SUCCESS;
1678}
1679
1680/**
1681 * Initializes a Shared Clipboard service's client state.
1682 *
1683 * @returns VBox status code.
1684 * @param pClientState Client state to initialize.
1685 * @param uClientID Client ID (HGCM) to use for this client state.
1686 */
1687static int vboxSvcClipboardClientStateInit(PVBOXCLIPBOARDCLIENTSTATE pClientState, uint32_t uClientID)
1688{
1689 LogFlowFuncEnter();
1690
1691 vboxSvcClipboardClientStateReset(pClientState);
1692
1693 /* Register the client.
1694 * Note: Do *not* memset the struct, as it contains classes (for caching). */
1695 pClientState->u32ClientID = uClientID;
1696 pClientState->uProtocolVer = 0;
1697
1698 return VINF_SUCCESS;
1699}
1700
1701/**
1702 * Destroys a Shared Clipboard service's client state.
1703 *
1704 * @returns VBox status code.
1705 * @param pClientState Client state to destroy.
1706 */
1707static int vboxSvcClipboardClientStateDestroy(PVBOXCLIPBOARDCLIENTSTATE pClientState)
1708{
1709 RT_NOREF(pClientState);
1710
1711 LogFlowFuncEnter();
1712
1713 return VINF_SUCCESS;
1714}
1715
1716/**
1717 * Resets the Shared Clipboard data reading porition of the old client state.
1718 *
1719 * Legacy protocol, do not use anymore.
1720 *
1721 * @param pClientState Client state to reset data reading portion for.
1722 */
1723void vboxSvcClipboardOldClientStateResetData(PVBOXCLIPBOARDCLIENTSTATE pClientState)
1724{
1725 if (pClientState->Old.data.pv)
1726 {
1727 RTMemFree(pClientState->Old.data.pv);
1728 pClientState->Old.data.pv = NULL;
1729 }
1730
1731 pClientState->Old.data.cb = 0;
1732 pClientState->Old.data.u32Format = 0;
1733}
1734
1735/**
1736 * Resets a Shared Clipboard service's old client state.
1737 * Legacy protocol, do not use anymore.
1738 *
1739 * @param pClientState Client state to reset.
1740 */
1741void vboxSvcClipboardOldClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pClientState)
1742{
1743 LogFlowFuncEnter();
1744
1745 pClientState->Old.fAsync = false;
1746 pClientState->Old.fReadPending = false;
1747
1748 pClientState->Old.fHostMsgQuit = false;
1749 pClientState->Old.fHostMsgReadData = false;
1750 pClientState->Old.fHostMsgFormats = false;
1751
1752 pClientState->Old.u32AvailableFormats = 0;
1753 pClientState->Old.u32RequestedFormat = 0;
1754
1755 vboxSvcClipboardOldClientStateResetData(pClientState);
1756}
1757
1758/**
1759 * Resets a Shared Clipboard service's client state.
1760 *
1761 * @param pClientState Client state to reset.
1762 */
1763static void vboxSvcClipboardClientStateReset(PVBOXCLIPBOARDCLIENTSTATE pClientState)
1764{
1765 LogFlowFuncEnter();
1766
1767#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1768 pClientState->URI.fTransferStart = false;
1769 pClientState->URI.enmTransferDir = SHAREDCLIPBOARDURITRANSFERDIR_UNKNOWN;
1770#endif
1771
1772 vboxSvcClipboardOldClientStateReset(pClientState);
1773}
1774
1775/*
1776 * We differentiate between a function handler for the guest and one for the host.
1777 */
1778static DECLCALLBACK(int) svcHostCall(void *,
1779 uint32_t u32Function,
1780 uint32_t cParms,
1781 VBOXHGCMSVCPARM paParms[])
1782{
1783 int rc = VINF_SUCCESS;
1784
1785 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1786 u32Function, VBoxClipboardHostMsgToStr(u32Function), cParms, paParms));
1787
1788 switch (u32Function)
1789 {
1790 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE:
1791 {
1792 if (cParms != 1)
1793 {
1794 rc = VERR_INVALID_PARAMETER;
1795 }
1796 else
1797 {
1798 uint32_t u32Mode = VBOX_SHARED_CLIPBOARD_MODE_OFF;
1799
1800 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
1801 if (RT_SUCCESS(rc))
1802 rc = vboxSvcClipboardModeSet(u32Mode);
1803 }
1804
1805 break;
1806 }
1807
1808 case VBOX_SHARED_CLIPBOARD_HOST_FN_SET_HEADLESS:
1809 {
1810 if (cParms != 1)
1811 {
1812 rc = VERR_INVALID_PARAMETER;
1813 }
1814 else
1815 {
1816 uint32_t uHeadless;
1817 rc = HGCMSvcGetU32(&paParms[0], &uHeadless);
1818 if (RT_SUCCESS(rc))
1819 {
1820 g_fHeadless = RT_BOOL(uHeadless);
1821 LogRel(("Shared Clipboard: Service running in %s mode\n", g_fHeadless ? "headless" : "normal"));
1822 }
1823 }
1824 break;
1825 }
1826
1827 default:
1828 {
1829#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
1830 rc = vboxSvcClipboardURIHostHandler(u32Function, cParms, paParms);
1831#else
1832 rc = VERR_NOT_IMPLEMENTED;
1833#endif
1834 break;
1835 }
1836 }
1837
1838 LogFlowFuncLeaveRC(rc);
1839 return rc;
1840}
1841
1842#ifndef UNIT_TEST
1843/**
1844 * SSM descriptor table for the VBOXCLIPBOARDCLIENTDATA structure.
1845 */
1846static SSMFIELD const g_aClipboardClientDataFields[] =
1847{
1848 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, u32ClientID), /* for validation purposes */
1849 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, Old.fHostMsgQuit),
1850 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, Old.fHostMsgReadData),
1851 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, Old.fHostMsgFormats),
1852 SSMFIELD_ENTRY(VBOXCLIPBOARDCLIENTSTATE, Old.u32RequestedFormat),
1853 SSMFIELD_ENTRY_TERM()
1854};
1855#endif
1856
1857static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
1858{
1859 RT_NOREF(u32ClientID);
1860
1861#ifndef UNIT_TEST
1862 /*
1863 * When the state will be restored, pending requests will be reissued
1864 * by VMMDev. The service therefore must save state as if there were no
1865 * pending request.
1866 * Pending requests, if any, will be completed in svcDisconnect.
1867 */
1868 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1869
1870 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)pvClient;
1871
1872 /* This field used to be the length. We're using it as a version field
1873 with the high bit set. */
1874 SSMR3PutU32(pSSM, UINT32_C(0x80000002));
1875 int rc = SSMR3PutStructEx(pSSM, &pClient->State, sizeof(pClient->State),
1876 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1877 AssertRCReturn(rc, rc);
1878
1879#else /* UNIT_TEST */
1880 RT_NOREF3(u32ClientID, pvClient, pSSM);
1881#endif /* UNIT_TEST */
1882 return VINF_SUCCESS;
1883}
1884
1885static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
1886{
1887#ifndef UNIT_TEST
1888 RT_NOREF(u32ClientID, uVersion);
1889
1890 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1891
1892 PVBOXCLIPBOARDCLIENT pClient = (PVBOXCLIPBOARDCLIENT)pvClient;
1893 AssertPtr(pClient);
1894
1895 /* Existing client can not be in async state yet. */
1896 Assert(!pClient->State.Old.fAsync);
1897
1898 /* Save the client ID for data validation. */
1899 /** @todo isn't this the same as u32ClientID? Playing safe for now... */
1900 uint32_t const u32ClientIDOld = pClient->State.u32ClientID;
1901
1902 /* Restore the client data. */
1903 uint32_t lenOrVer;
1904 int rc = SSMR3GetU32(pSSM, &lenOrVer);
1905 AssertRCReturn(rc, rc);
1906 if (lenOrVer == UINT32_C(0x80000002))
1907 {
1908 rc = SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State),
1909 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
1910 AssertRCReturn(rc, rc);
1911 }
1912 else
1913 {
1914 LogFunc(("Client data size mismatch: got %#x\n", lenOrVer));
1915 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1916 }
1917
1918 /* Verify the client ID. */
1919 if (pClient->State.u32ClientID != u32ClientIDOld)
1920 {
1921 LogFunc(("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->State.u32ClientID));
1922 pClient->State.u32ClientID = u32ClientIDOld;
1923 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
1924 }
1925
1926 /* Actual host data are to be reported to guest (SYNC). */
1927 VBoxClipboardSvcImplSync(pClient);
1928
1929#else /* UNIT_TEST*/
1930 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
1931#endif /* UNIT_TEST */
1932 return VINF_SUCCESS;
1933}
1934
1935static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
1936{
1937 RT_NOREF(pvData, cbData);
1938
1939 LogFlowFunc(("u32Function=%RU32\n", u32Function));
1940
1941 int rc = VINF_SUCCESS;
1942
1943 /* Figure out if the client in charge for the service extension still is connected. */
1944 ClipboardClientMap::const_iterator itClient = g_mapClients.find(g_ExtState.uClientID);
1945 if (itClient != g_mapClients.end())
1946 {
1947 PVBOXCLIPBOARDCLIENT pClient = itClient->second;
1948 AssertPtr(pClient);
1949
1950 switch (u32Function)
1951 {
1952 /* The service extension announces formats to the guest. */
1953 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
1954 {
1955 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_ExtState.fReadingData=%RTbool\n", g_ExtState.fReadingData));
1956 if (g_ExtState.fReadingData)
1957 {
1958 g_ExtState.fDelayedAnnouncement = true;
1959 g_ExtState.uDelayedFormats = u32Format;
1960 }
1961 else
1962 {
1963 rc = vboxSvcClipboardOldReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_FORMATS_WRITE, u32Format);
1964 }
1965
1966 break;
1967 }
1968
1969 /* The service extension wants read data from the guest. */
1970 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
1971 {
1972 rc = vboxSvcClipboardOldReportMsg(pClient, VBOX_SHARED_CLIPBOARD_HOST_MSG_READ_DATA, u32Format);
1973 break;
1974 }
1975
1976 default:
1977 /* Just skip other messages. */
1978 break;
1979 }
1980 }
1981 else
1982 rc = VERR_NOT_FOUND;
1983
1984 LogFlowFuncLeaveRC(rc);
1985 return rc;
1986}
1987
1988static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
1989{
1990 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
1991
1992 VBOXCLIPBOARDEXTPARMS parms;
1993 RT_ZERO(parms);
1994
1995 if (pfnExtension)
1996 {
1997 /* Install extension. */
1998 g_ExtState.pfnExtension = pfnExtension;
1999 g_ExtState.pvExtension = pvExtension;
2000
2001 parms.u.pfnCallback = extCallback;
2002
2003 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2004 }
2005 else
2006 {
2007 if (g_ExtState.pfnExtension)
2008 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2009
2010 /* Uninstall extension. */
2011 g_ExtState.pfnExtension = NULL;
2012 g_ExtState.pvExtension = NULL;
2013 }
2014
2015 return VINF_SUCCESS;
2016}
2017
2018extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
2019{
2020 int rc = VINF_SUCCESS;
2021
2022 LogFlowFunc(("ptable=%p\n", ptable));
2023
2024 if (!ptable)
2025 {
2026 rc = VERR_INVALID_PARAMETER;
2027 }
2028 else
2029 {
2030 LogFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
2031
2032 if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2033 || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
2034 {
2035 rc = VERR_INVALID_PARAMETER;
2036 }
2037 else
2038 {
2039 g_pHelpers = ptable->pHelpers;
2040
2041 ptable->cbClient = sizeof(VBOXCLIPBOARDCLIENT);
2042
2043 ptable->pfnUnload = svcUnload;
2044 ptable->pfnConnect = svcConnect;
2045 ptable->pfnDisconnect = svcDisconnect;
2046 ptable->pfnCall = svcCall;
2047 ptable->pfnHostCall = svcHostCall;
2048 ptable->pfnSaveState = svcSaveState;
2049 ptable->pfnLoadState = svcLoadState;
2050 ptable->pfnRegisterExtension = svcRegisterExtension;
2051 ptable->pfnNotify = NULL;
2052 ptable->pvService = NULL;
2053
2054 /* Service specific initialization. */
2055 rc = svcInit();
2056 }
2057 }
2058
2059 return rc;
2060}
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